Part 1 of this four-part series provided a solid grounding in both GWT and XForms and explored how the two can work together to streamline the process of creating Web applications. Now in Part 2, you'll build a simple Web application, the rock star application, that has a two pages: one for viewing artists and one for viewing the albums recorded by those artists. The first page will be built using GWT, and it will use GWT's widgets and GWT's Ajax abstractions. It will link to your second page, which will be built using XForms, and it will use an XForms data model and XForms controls for creating its UI.
This article uses GWT version 1.4 and the Mozilla XForms plugin 0.8 (see the Resources for download links). The Mozilla XForms plugin works with any Mozilla-based Web browser, such as Firefox and Seamonkey. GWT requires knowledge of the Java™ language, and Web technologies such as HTML and CSS. This article makes heavy use of JavaScript, as well. XForms makes heavy use of the Model-View-Control paradigm, so familiarity with that is helpful. Prior exposure to XForms and GWT is helpful of course, but is not necessary. The code in this article was developed using Eclipse 3.3, but it is not necessary to know Eclipse.
The first thing you'll need to do in your rock star application is get a list of all of your artists. Of course, you'll also want to be able to add new artists to this list. Your list will also give you a way to navigate to your second page, where you'll be able to manage the albums recorded by a particular artist. You'll use GWT for the artist page. You'll make use of its built-in widgets for creating a nice UI for your page, and its Ajax abstractions for simplifying the loading and saving of data.
The data in your application will be stored in simple XML files. XML is a common format for transferring data, and it is, of course, native to XForms. You could easily keep the data in a relational database, though there is a good chance you'd wind up serializing it as XML for various purposes anyway. Using XML instead of a relational database will keep it simple so you can focus on the real stars of the application: GWT and XForms.
You'll need a simple data model for our application to represent an artist. With GWT, this is as easy as creating a simple Java bean, as shown in Listing 1.
Listing 1. Artist model: Java Bean
package org.developerworks.rockstar.client;
import com.google.gwt.user.client.rpc.IsSerializable;
public class Artist implements IsSerializable{
private int id;
private String name;
private String genre;
public Artist(){
// needed for GWT's RPC mechanism
}
public Artist(int id, String name, String genre) {
this.id = id;
this.name = name;
this.genre = genre;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
}
|
As mentioned, this is just a standard Java bean: three fields with corresponding getters and setters. You'll use a numeric ID for your artists just to make it easy to reference them. This is also what you'd probably have if there was a database table being used to persist the artists. Notice that this is in the client package of your application. Thus this class will be compiled into JavaScript. That doesn't matter to you, though. You can still program it in Java, paying no attention to the JavaScript behind the curtain. Finally, notice that your class implements the IsSerializable marker interface. This is needed for any class that is going to be sent over the wire, for example, as part of a request or a response on an Ajax call. Now let's examine how you can use GWT to create a UI for a list of Artist objects.
Use GWT widgets for artist list
One of the many nice things about GWT is that it comes with a set of widgets for creating common UI structures. You get to use familiar Java syntax for creating UI elements. If you've done Swing or SWT programming, this is very familiar. In this case, you will use a FlexTable. This is a table that is dynamic and is well suited for your application because the number of artists is not set. The FlexTable will expand to fit the number of artists. The code for the populating the FlexTable is shown in Listing 2.
Listing 2. Creating an artist table
private void populateTable(Artist[] artists){
// clear the table
int rowCount = this.artistTable.getRowCount();
for (int i=0;i<rowCount;i++){
this.artistTable.removeRow(i);
}
// create the header
this.artistTable.getRowFormatter().addStyleName(0, "tableHeader");
this.artistTable.setText(0, 0, "Name");
this.artistTable.setText(0, 1, "Genre");
// now add artists
for (int i=0;i<artists.length;i++){
this.artistTable.setText(i+1, 0, artists[i].getName());
this.artistTable.setText(i+1, 1, artists[i].getGenre());
}
this.artistTable.setBorderWidth(4);
}
|
The code in Listing 2 clears the table if it's already there. Then it creates a header. The only thing really special about the header is that you attach a style to it. Let's briefly take a look at that. Next the code iterates over the artists, adding them to the table. Let's take a look at the page it adds the table to in Listing 3.
Listing 3. The artist page
<html>
<head>
<title>RockStars</title>
<!-- -->
<!-- This script loads your compiled module. -->
<!-- If you add any GWT meta tags, they must -->
<!-- be added before this line. -->
<!-- -->
<script language='javascript' src='org.developerworks.
rockstar.RockStarMain.nocache.js'></script>
<style type="text/css">
.tableHeader{
background-color:#AAAAAA;
}
</style>
</head>
<body>
</body>
</html>
|
There's not much to the HTML! You let GWT do all the hard work. The only thing HTML-ish you've done is inlined some CSS for the header on your table. Now that you have the UI elements for your page, you just need some data. You'll get that using Ajax, GWT style.
Getting artist data: Using a Remote Procedure Call
You have a nice GWT widget ready to use for displaying your list of artists. Now you just need a list. To do this you'll create a service for managing artists. You'll use GWT to invoke this service asynchronously, for example, using Ajax. This is a typical GWT Remote Procedure Call (RPC). Start off by declaring an interface for the service, as shown in Listing 4.
Listing 4. Artist Service Interface
package org.developerworks.rockstar.client;
import com.google.gwt.user.client.rpc.RemoteService;
public interface ArtistService extends RemoteService {
public Artist[] getAllArtists();
public void addArtist(Artist newArtist);
}
|
Note that your service extends the GWT marker interface RemoteService. This is a necessary GWT convention. Also note that your getAllArtists() call returns an array of Artist objects. These are the same Artist objects defined earlier as your data model. You might be tempted to have this return a Collection<Artist> or List<Artist> but don't do it. GWT does not support generics in client code, for example, code that will be compiled into JavaScript. There are no generics in JavaScript, and generic-type information is not available at runtime, anyway. You could still return a java.util.List, as these are allowed, but an array works just as well and is strongly typed.
GWT requires an asynchronous version of any interface that's going to be invoked from the client. The corresponding asynchronous version of our interface is shown in Listing 5.
Listing 5. Asynchronous service interface
package org.developerworks.rockstar.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface ArtistServiceAsync {
public void getAllArtists(AsyncCallback callback);
public void addArtist(Artist newArtist, AsyncCallback callback);
}
|
The key here is the Async suffix on the service name. This is a naming convention that lets GWT match up this interface to the one shown in Listing 4. Also note how all the calls have been made asynchronous, meaning they have a void return type. They all have an AsyncCallback that allows GWT to invoke the callback once the server-side implementation of the interface finishes processing the request made to it. Let's take a look at the server-side implementation of our interface in Listing 6.
Listing 6. Server-side implementation of interface
package org.developerworks.rockstar.server;
import java.util.List;
import org.developerworks.rockstar.client.Artist;
import org.developerworks.rockstar.client.ArtistService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
public class ArtistServiceImpl extends RemoteServiceServlet implements ArtistService {
private static final long serialVersionUID = -1801240935065207659L;
private List<Artist> artists;
private ArtistDao dao;
public ArtistServiceImpl(){
this.dao = new ArtistFileDao();
this.artists = this.dao.getAllArtists();
}
public void addArtist(Artist newArtist) {
newArtist.setId(this.artists.size());
this.artists.add(newArtist);
dao.saveArtists(this.artists);
}
public Artist[] getAllArtists() {
Artist[] array = new Artist[this.artists.size()];
return this.artists.toArray(array);
}
}
|
There are a few things you should notice about this class. One is that it extends RemoteServiceServlet as well as implementing the interface you defined. The RemoteServiceServlet is a classic Java servlet designed to handle Ajax requests. At runtime, the superclass's methods will be invoked by requests from our client code, and the superclass will use reflection to invoke the actual method requested by the client. Finally, note that you are using a Data Access Object (DAO) interface, ArtistDao. You are using a file-based implementation, ArtistFileDao, but you could easily swap this out for a database-based implementation. That class also has all the messy code for reading/writing to our files as well as parsing XML. Let's give our app a test.
So far everything has been pure GWT. One of the nice things with GWT is that you can use hosted mode. If you're using Eclipse, you can launch the application from there and view it. First you'll need some data, though. A simple data file is shown in Listing 7.
Listing 7. Test data
<?xml version="1.0" encoding="UTF-8"?>
<Data>
<Artist>
<Id>0</Id>
<Name>The Struts Five</Name>
<Genre>Classic Rock</Genre>
</Artist>
<Artist>
<Id>1</Id>
<Name>Spring Flow</Name>
<Genre>Techno</Genre>
</Artist>
<Artist>
<Id>2</Id>
<Name>The Holy Grails</Name>
<Genre>Funk</Genre>
</Artist>
<Artist>
<Id>3</Id>
<Name>The Rails Way</Name>
<Genre>Pop</Genre>
</Artist>
<Artist>
<Id>4</Id>
<Name>Cake Clone</Name>
<Genre>Pop</Genre>
</Artist>
<Artist>
<Id>5</Id>
<Name>Obscure Tapestry</Name>
<Genre>Techno</Genre>
</Artist>
<Artist>
<Id>6</Id>
<Name>Dojo Darling</Name>
<Genre>Classic Rock</Genre>
</Artist>
<Artist>
<Id>7</Id>
<Name>Cairingorm</Name>
<Genre>Progressive</Genre>
</Artist>
<Artist>
<Id>8</Id>
<Name>ProtoStripes</Name>
<Genre>Thrash</Genre>
</Artist>
</Data>
|
Now that you have test data, you can launch your application. It should bring up an interface like the one shown in Figure 1.
Figure 1. Viewing artists in hosted mode
There's your list of artists from the test data. Now you just need a simple form to enter in a new artist.
Adding a new artist: Using GWT to create a form
You need a simple data-entry form to enter in your new artists. Luckily GWT also includes widgets for creating forms. With GWT, you can create your form programmatically, as shown in Listing 8.
Listing 8. Data entry form with GWT
public class RockStarMain implements EntryPoint {
// Widgets for the page
final FlexTable artistTable = new FlexTable();
final VerticalPanel outerPanel = new VerticalPanel();
final HorizontalPanel formPanel = new HorizontalPanel();
final Label artistLabel = new Label("Artist Name:");
final TextBox artistInput = new TextBox();
final Label genreLabel = new Label("Genre:");
final TextBox genreInput = new TextBox();
final Button addButton = new Button("Add Artist");
/**
* This is the entry point method.
*/
public void onModuleLoad() {
// add the outer panel, then add to it
RootPanel.get().add(outerPanel);
outerPanel.add(artistTable);
outerPanel.add(formPanel);
// arrange form elements horizontally
formPanel.add(artistLabel);
formPanel.add(artistInput);
formPanel.add(genreLabel);
formPanel.add(genreInput);
formPanel.add(addButton);
// add event listener to our button
ClickListener listener = new ClickListener(){
public void onClick(Widget sender) {
addNewAritst();
}
};
addButton.addClickListener(listener);
// load the artists now all the widgets are ready
this.loadArtists();
}
|
This code is once again refreshingly straightforward. You create a set of widgets: labels, text boxes, and buttons (and the FlexTable you've been working with.) You also use a couple of panels that act like layout managers for laying out your widgets. You then add an event listener to one of your buttons for adding a new artist. The event listener will invoke your back-end service. Let's take a look at it in Listing 9.
Listing 9. Add new artist method
private void addNewAritst(){
Artist artist = new Artist(-1, artistInput.getText(), genreInput.getText());
ArtistServiceAsync artistService = getArtistService();
AsyncCallback callback = new AsyncCallback(){
public void onFailure(Throwable caught) {
// remove last row because of failure
removeLastArtist();
}
public void onSuccess(Object result) {
// nothing to do here since we added optimistically
}
};
artistService.addArtist(artist, callback);
// we'll be optimistic and go ahead and add to the table
int size = this.artistTable.getRowCount();
this.artistTable.setText(size, 0, artist.getName());
this.artistTable.setText(size, 1, artist.getGenre());
}
|
In this code you see the asynchronous version of your interface being called. An anonymous callback method is created. In this case it is very simple, and only does anything if the call fails. That's because you go ahead and add the artist to your table without waiting for the server. This provides a good user experience as adding artists will be instantaneous, but it assumes that failures will be very rare. The full source code is available for download at the end of the article. Let's take a look at your UI now that you've added the form, as shown in Figure 2.
Figure 2. View artists and add new artists
Fill out the form and click the Add Artist button. Your UI should be instantly updated, as shown in Figure 3.
Figure 3. Artist added
You've seen how GWT can be used for creating UI elements and using Ajax to retrieve and save data to back-end services. Let's take a look at how you can use XForms to create a page for managing albums.
For the second part of your application, you'll view a list of albums by an artist. Obviously you want to create a single page for displaying albums that can be used for each artist. So how will you know which albums to show? There are obviously a few different ways to do this, but you'll use a command pattern. The command should tell you everything you need to know to create the page. In this case you'll need to know whose albums to show, in other words, which artist's albums to show. To do this you'll need a request parameter that specifies an artist, so you'll just call it artistId. You'll need a link to your second page from the first page, and that link will need to have the artistId parameter.
Linking from GWT page to XForms page
Let's go back to your first page and a create a link to your second page. This will only take a small change to the code, as shown in Listing 10.
Listing 10. Adding links to the Artist list
private void populateTable(Artist[] artists){
// clear the table
int rowCount = this.artistTable.getRowCount();
for (int i=0;i<rowCount;i++){
this.artistTable.removeRow(i);
}
// create the header
this.artistTable.getRowFormatter().addStyleName(0, "tableHeader");
this.artistTable.setText(0, 0, "Name");
this.artistTable.setText(0, 1, "Genre");
// now add artists
for (int i=0;i<artists.length;i++){
//this.artistTable.setText(i+1, 0, artists[i].getName());
String html = "<a href=\"Albums.jsp?artistId=
"+artists[i].getId()+"\">"+artists[i].getName()+"</a>";
this.artistTable.setHTML(i+1, 0, html);
this.artistTable.setText(i+1, 1, artists[i].getGenre());
}
this.artistTable.setBorderWidth(4);
}
|
All that you changed is the code that sets the left column in your table. Instead of using setText(...), you used setHTML(...). This allows you to put HTML in the table, and thus create a simple link to a new page "Albums.jsp". You used a JSP so that way you can do some dynamic construction of the page. The dynamic part is that you only want to show the albums recorded by the artist specified via the artistId. Notice that the artistId parameter is part of your link. Now you just need to create this page.
Creating an XForms page using GWT
You can create the JSP just like any other Web resource such as HTML or CSS pages. You can still use GWT on your page. All you have to do is reference the JavaScript file that GWT generates. For now you'll use server-side code to load the albums recorded by the specified artist. You'll write this data directly to your XForms model instance data, as shown in Listing 11.
Listing 11. Inline data using server logic
<?xml version="1.0" encoding="UTF-8"?>
<xhtml:html xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<%@page import="org.developerworks.rockstar.client.*" %>
<%@page import="org.developerworks.rockstar.server.*" %>
<%@page import="java.util.List" %>
<xhtml:head>
<xhtml:title>Albums</xhtml:title>
<xforms:model id="albums">
<xforms:instance id="albumData" xmlns="">
<Data>
<%
int artistId = Integer.parseInt(request.getParameter("artistId"));
AlbumDao dao = new AlbumFileDao();
List<Album> albums = dao.getAllAlbums();
for (Album album : albums){
if (album.getArtistId() == artistId){
%>
<Album>
<Title><%= album.getTitle() %></Title>
<Year><%= album.getYear() %></Year>
</Album>
<%
}
}
%>
</Data>
</xforms:instance>
</xforms:model>
</xhtml:head>
<xhtml:body>
<xhtml:div id="albumList">
<xforms:repeat id="repeatItem" nodeset="/Data/Album">
<xhtml:div>
<xforms:output ref="Title">
<xforms:label>Title:</xforms:label>
</xforms:output>
<xforms:output ref="Year">
<xforms:label>Year:</xforms:label>
</xforms:output>
</xhtml:div>
</xforms:repeat>
</xhtml:div>
</xhtml:body>
</xhtml:html>
|
The Java scriptlet handles the logic to figure out which albums to show. Again you are loading this data from an XML file. The filtering logic is contained in an external class that can be downloaded as part of the source code for this article. Note once again you'll create some test data in an XML file. The test data is also included with the article's source code.
Testing XForms pages in Web mode
Now you're ready to test your XForms albums page. So far you've been testing your GWT artists page using GWT's hosted dode. You can launch in hosted mode once again, but you'll need to switch over to GWT's Web Mode for testing your new page. Why is this? XForms relies on a browser plugin that would not be present in GWT's hosted mode. Figure 4 shows how to switch to Web mode.
Figure 4. Switching to Web mode
Clicking on Compile/Browse will bring up the application in Web mode, as shown in Figure 5.
Figure 5. Application in Web Mode
You should see the application now running in your default Web browser. Now you can click on one of the artists and you should see the interface shown in Figure 6.
Figure 6. Testing the Artists Page
Your page shows a list of albums for the artist. It uses some simple XForms controls to iterate the XML data from the model's instance data. In Part 3 you'll see how you can add XForms controls for data entry and wire this to GWT Ajax calls to allow for record company executives to add new albums on this page.
In this article, Part 2 of the rock star series, you've seen how you can build an interactive page using GWT and link it to a page that uses XForms. In Part 3, you'll refactor the rock star application built in this part. You won't actually change the functionality of the application created here, but you'll see how you can start using GWT elements on the same page as XForms elements to make the application more interactive.
| Description | Name | Size | Download method |
|---|---|---|---|
| Part 2 sample code | rockstar2_src.zip | 12KB | HTTP |
Information about download methods
Learn
- Don't miss
Part 1, Introducing GWT's JavaScript Native Interface of this article series, Integrate XForms with the Google Web Toolkit.
-
For a great introduction to XForms read the three-part series Introduction to XForms (Chris Herbroth, developerWorks, September 2006).
-
Learn more about using JavaScript and XForms together in XForms tip: Call JavaScript from an XForms form (Nicholas Chase, developerWorks, January 2007).
-
See how JavaScript can improve the functionality of XForms in the article Use JavaScript to make your XForms more robust (Michael Galpin, developerWorks, July 2007).
-
Learn how XForms can work with Ajax to create auto-suggest functionality in the article Use XForms and Ajax to create an autosuggest form field (Michael Galpin, developerWorks, July 2007).
-
Read one of the first in-depth tutorials on GWT in the article Ajax for Java
developers: Exploring the Google Web Toolkit (Philip McCarthy, developerWorks, June 2006).
-
Learn more about building Web applications with GWT in the four-part developerWorks tutorial series Build an Ajax application using Google Web Toolkit, Apache Derby, and Eclipse (Noel Rappin, developerWorks, December 2006).
-
Get familiar with GWT's Ajax capabilities in the two-part tutorial series Build an Ajax-enabled application using the Google Web Toolkit and Apache Geronimo (Michael Galpin, developerWorks, May 2007).
-
Check out the IBM developerWorks Ajax Resource Center.
-
Check out the IBM developerworks Java technology zone.
-
Visit the XForms home at W3C.
-
IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
-
XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
-
developerWorks technical events and webcasts: Stay current with technology in these sessions.
-
Learn more about XForms in the IBM developerWorks XML zone.
-
Podcasts: Tune in and catch up with IBM technical experts.
Get products and technologies
-
The best place for GWT information is straight from the source, at the official Google Web Toolkit site.
-
Get the XForms extension for Mozilla, Firefox, or Seamonkey.
Discuss
Comments (Undergoing maintenance)





