Platinum Partner

GWT: Building A Model

The model layer of a GWT application has to be a little bit smarter than the model in many traditional web applications. It needs to be responsible for notifying the view layer of changes, as well as for receiving updates to its own structure. In desktop Java development, this is accomplished through the use of PropertyChangeEvents and PropertyChangeListeners that bind the data from the model to the view components. The view layer "listens" for changes to the model and updates itself accordingly, using the Observer pattern. This can be done in GWT development as well. Figure 1 shows a basic User class that we will work with. It is a simple class containing a user name and other common fields, as well as two Address objects (one shipping and one billing). To get started, we need to enable these for data binding.


[img_assist|nid=3421|title=|desc=|link=url|url=|align=right|width=208|height=388]Figure 1 The User and Address classes serving as the model. Notice the PropertyChangeSupport attribute, which we will use to notify the view of changes to the model beans.


We need to enable data binding to user interface elements in our model.


The basis of data binding between the model and the view is triggering events in the model that notify the view of changes, and vice versa. We will use the java.beans.PropertyChangeSupport class. This isnâ€™t a class provided by GWT yet, but it is available as part of the GWTx project (, which adds to the core Java classes provided by GWT. Listing 1 shows the User object instrumented with the PropertyChangeSupport class.

Listing 1 Using the PropertyChangeSupport class in the User object:

public class User implements IsSerializable {                          

private String username;
private String firstName;
private String lastName;
private Address billingAddress = new Address();
private Address shippingAddress = new Address();
private String password;
private String notes;
private transient PropertyChangeSupport changes =
new PropertyChangeSupport(this);

public User() {

public String getFirstName() {
return firstName;

public void setFirstName(String firstName) {
String old = this.firstName;
this.firstName = firstName;
"firstName", old, firstName);

public String getLastName() {
return lastName;

public void setLastName(String lastName) {
String old = lastName;
this.lastName = lastName;
changes.firePropertyChange("lastName", old, lastName );

public Address getBillingAddress() {
return billingAddress;

public void setBillingAddress(Address billingAddress) {
Address old = this.billingAddress;
this.billingAddress = billingAddress;
"billingAddress", old, billingAddress);

// The rest of the getters and setters are removed for brevity.

public void addPropertyChangeListener(
PropertyChangeListener l) {

public void addPropertyChangeListener(
String propertyName,
PropertyChangeListener l) {
propertyName, l);

public void removePropertyChangeListener(PropertyChangeListener l) {

public void removePropertyChangeListener(
String propertyName, PropertyChangeListener l) {
changes.removePropertyChangeListener(propertyName, l);


This gives us a model object capable of notifying the view of changes and exposing methods for attaching listeners.


This may be a lot more code than you are used to seeing in a model bean in a traditional web application, but it is mostly boilerplate. We first need to implement IsSerializable or Serializable (the GWT marker interfaces that tell the compiler this class should be made serializable), because we want this object to move back and forth between the client and the server. This time, though, we have a nonserializable property: the PropertyChangeSupport instance changes is marked transient and isn't part of the serialized version that will be sent back and forth. It is constructed each time with the object.

Once we have the PropertyChangeSupport class, we need to instrument the setter methods to fire change events. The sequence is repetitive, but very important. First you store the current value, then you set the new value, and only after the new value is set do you fire the change event . It is critical that the change events only be fired after the instance value has been set, not at the top of the setter method. The reason for this is what happens inside the PropertyChangeSupport class.

PropertyChangeSupport (PCS) provides collections and fires change events much like the interfaces used in the previous calculator example. It also checks each call to firePropertyChange() to make sure the old and new values are different before firing the event. Figure 2 shows the execution sequence for a change event being fired and updating an element with a two-way bind.


Figure 2 The flow of property change events firing will loop if not checked by the model. If the model doesn't check the change for equality, or if the change hasn't happened yet, an infinite loop is formed.

If you called firePropertyChange() at the top of the setter method, when the last step in this sequence was reached, the current instance value on the model object would still be the same, and the whole event cycle would go into an infinite loop!

Finally, we need to provide methods on the model object for listeners to be added to the model. There are two kinds of listeners supported by the PropertyChangeSupport class. Global listeners will receive an event any time a change is fired. The listener would then look at the value returned by the call to getPropertyName() on the PropertyChangeEvent instance to decide what to do. The other type of listeners are property specific . These will only be fired if the specified property changes, and they are generally the most useful listeners. However, if you have a complex model or one that requires a lot of translation between the model data and the data needed to properly render the view elements, it can be easier to just listen for any change and reset the entire view component.

This article is excerpted from Chapter 1 of GWT in Practice, by Robert Cooper and Charlie Collins, and published in May 2008 by Manning Publications.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ }}
{{ parent.authors[0].realName ||}}


{{ parent.views }} ViewsClicks