High-performance Web development with Google Web Toolkit and Eclipse Galileo

By now, you have probably heard of Google Web Toolkit (GWT). You know that it lets you write your Web applications in the Java™ programming language that is compiled into JavaScript to run in Web browsers. This lets you be more productive by taking advantage of Java's static typing and great tools like Eclipse. You have may seen some of the useful and stylish widgets built on top of GWT. What you may not know is that GWT lets you create high-performance Web applications. In this article, we look at how you can use the Google Plug-in with Eclipse Galileo to tap into the performance features of GWT, such as compiler optimizations, deferred binding, and Ajax optimizations. Developer performance is still an important part of GWT, so along the way, we will also show you how tweak the Google Plug-in for Eclipse to increase your productivity.

Share:

Michael Galpin, Software architect, eBay

Michael_GalpinMichael Galpin is an architect at eBay and is a frequent contributor to developerWorks. He has spoken at various technical conferences, including JavaOne, EclipseCon and AjaxWorld. To get a preview of what he is working on, follow @michaelg on Twitter.



20 October 2009

Also available in Japanese

Prerequisites

In this article, we will take a look at several features of GWT and examine how they enable you to build high-performance Web applications. This article is not an introduction to GWT, so prior experience with GWT is assumed. It is also assumed that you know Java technology and that you are familiar with JavaScript, CSS, and HTML. It is recommended that you use the latest version of GWT: V1.7 was used for this article. This article also uses the Google Plug-in for Eclipse. V1.1 of the Google Plug-in was used with Eclipse V3.5 (Galileo) for this article. We will also use the Firebug plug-in for Mozilla Firefox. V1.4.2 of Firebug was used, along with Firefox V3.5.2 (see Resources).


Faster JavaScript

GWT is well known for its ability to compile Java into JavaScript, enabling Java developers to develop dynamic Web applications. All the code is compiled into JavaScript, and many of GWT's performance features were designed to make JavaScript run faster in the browser. There are several such features in GWT, including browser-specific optimizations and streamlined Ajax, but most of the features start with GWT's Java-to-JavaScript compiler. So that is where we will begin our exploration of GWT's performance-related features.


Compiler optimizations

The GWT compiler is what translates your Java code into JavaScript that runs in the browser. However, it does much more than simply translate Java technology into JavaScript. The compiler does numerous optimizations to make your code run faster. However, it can be difficult to understand exactly what is going on with these optimizations, as the JavaScript emitted by GWT is obfuscated and very hard to read. First, we want to instruct the GWT compiler to produce some human-readable JavaScript, so we can get a better idea of exactly what optimizations the compiler is making.

The GWT compiler has three modes of operation. The default mode is obfuscated, in that the GWT compiler will emit obfuscated JavaScript. This JavaScript is not just difficult to read but it is compressed. This makes it smaller, so it loads faster over the Internet. The smaller size also helps the browser to parse the JavaScript quicker.

Some of you might be thinking that sending compressed JavaScript over the wire makes little difference since most Web servers send JavaScript using gzip compression, as gzip compression is supported by any modern Web browser. However, not only does the GWT compiler compress the JavaScript but it does so in a way that is engineered with gzip compression in mind. In other words, GWT-obfuscated JavaScript is highly gzip-compressible, despite being already compressed. Thus, if your Web server is not using gzip, you will obviously get a big speed boost by using GWT obfuscation. However, even if your Web server is using gzip, you will still get a significant boost from GWT obfuscation.

So for production code, we clearly want the GWT compiler set to emit obfuscated JavaScript. However, this clearly makes it nearly impossible to read the emitted JavaScript. To see this, take a look at some GWT-obfuscated JavaScript in Listing 1.

Listing 1. Obfuscated JavaScript
function qH(){return np}
function mH(){}
_=mH.prototype=new mu;_.gC=qH;_.tI=0;function
uH(){uH=ZH;sH={};tH=[];sH[LM]=[Is,Hs,Js];sH[JM]=[rt,qt,st];Xv(tH,yn,LM);Xv(tH,To,JM)}
var sH,tH;function AH(a){a.b=oH(new mH);return a}
function BH(a){var b,c,d,e,f,g,h,i,j,k;g=ox(new cx,MM);f=OA(new FA);j=XH(new
 VH,NM,OM);KA(f,j.b+sJ+j.c);pw(g.B,PM,true);Zw(gA(QM),f);Zw(gA(RM),g);f.B.focus()
;k=Jg(f.B,NJ).length;k>0&&JA(f,0,k);c=py(new my);Lf((tf(),c.b.B),SM);c.o=true;
b=ox(new cx,TM);b.B.id=UM;i=Py(new Ny);h=Ty(new My);d=UA(new RA);pw(d.B,VM,true);
VA(d,Uy(new My,WM));VA(d,i);VA(d,Uy(new My,XM));VA(d,h);d.b=(kz(),jz);VA(d,b);
Ax(c.j,d);Mx(c);vw(b,FH(new DH,c,g),(sh(),rh));e=KH(new IH,a,g,f,i,h,c,b);
vw(g,e,rh);vw(f,e,(hi(),gi))}
function CH(){return rp}
function xH(){}

Luckily, it is easy to coax the GWT compiler into creating some much more human-eadable JavaScript. You can simply pass the compiler a -style=PRETTY argument. This is made even easier using the Google Plug-in for Eclipse. When you trigger a GWT compile, it will bring up the dialog shown in Figure 1.

Figure 1. GWT compiler options
Screen shot showing GWT compiler options

To see the JavaScript that the compiler is emitting, just choose the Pretty setting shown in Figure 1. Now the code will look more like what is shown in Listing 2.

Listing 2. Pretty JavaScript
var $wnd = parent;
var $doc = $wnd.document;
var $moduleName, $moduleBase;
var $strongName = '21B409FCD39529C5A9DB925F7D8D9A95';
var $stats = $wnd.__gwtStatsEvent ? function(a) {return 
   $wnd.__gwtStatsEvent(a);} : null;
$stats && $stats({moduleName:'gwtperf',subSystem:'startup',evtGroup:
'moduleStartup',millis:(new Date()).getTime(),type:'moduleEvalStart'});
var _;
function nullMethod(){
}

function equals(other){
  return this === (other == null?null:other);
}

function getClass_0(){
  return Ljava_lang_Object_2_classLit;
}

function hashCode_0(){
  return this.$H || (this.$H = ++sNextHashId);
}

function toString_0(){
  return (this.typeMarker$ == nullMethod || this.typeId$ ==
2?this.getClass$():Lcom_google_gwt_core_client_JavaScriptObject_2_classLit)
.typeName + '@' + toPowerOfTwoString(this.typeMarker$ == nullMethod || this.typeId$
== 2?this.hashCode$():this.$H || (this.$H = ++sNextHashId), 4);
}

function Object_0(){
}

_ = Object_0.prototype = {};
_.equals$ = equals;
_.getClass$ = getClass_0;
_.hashCode$ = hashCode_0;
_.toString$ = toString_0;
_.toString = function(){
  return this.toString$();
}
;

This is still highly optimized JavaScript, but is much easier to understand. Of course, this makes a big difference in the size of the JavaScript being created. We can examine this by using the Firebug plug-in for Firefox, as shown in Figure 2.

Figure 2. Comparing JavaScript file size: Obfuscation vs. Pretty
Screen shot comparing JavaScript file size: Obfuscation vs. Pretty

Figure 2 shows the same GWT application (the starter project created by the Google Plug-in) compiled with obfuscated JavaScript (at the top) and compiled with pretty JavaScript (bottom.) As you can see from the figure, the JavaScript size goes from 58 KB to 146 KB when going from obfuscated to pretty.

Now we can take a look at some code to see how the GWT compiler optimizes it. One of the ideas behind GWT is that you can write your code using software engineering best practices. You can introduce the right kind of abstractions that will make your code robust and maintainable. Meanwhile, GWT will compile into very fast code. Let's take a sample trivial class for modeling users, as shown in Listing 3.

Listing 3. A Person class
public class Person {
    final String firstName;
    final String lastName;
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String getName(){
        return firstName + " " + lastName;
    }
}

Now, we will alter the following code from the GWT start project, shown in Listing 4.

Listing 4. Original GWT start project code
final TextBox nameField = new TextBox();
nameField.setText("GWT User");

Now we will simply use our Person object in place of the hard-coded string shown above. This change is shown in Listing 5.

Listing 5. Modified to use the Person
final TextBox nameField = new TextBox();
final Person user = new Person("GWT", "User");
nameField.setText(user.getName());

This is a simple change, but it is easy to imagine this being a useful abstraction for an application. However, there could be a price to pay in terms of performance. There is (potential) overhead in object creation and in method dispatch. Let's take a look at how the GWT compiler can help out. Listing 6 shows the compiled version of the original code.

Listing 6. Original code, compiled to JavaScript
nameField = $TextBox(new TextBox());
nameField.element['value'] = 'GWT User' != null?'GWT User':'';

Now let's take a look at what Listing 5 looks like when compiled to JavaScript. This is shown in Listing 7.

Listing 7. Modified code, compiled to JavaScript
  user = $Person(new Person(), 'GWT', 'User');
  $setText_1(nameField, user.firstName + ' ' + user.lastName);

The first line of code is calling the JavaScript constructor for the Person class. This is a straight translation of the Java code. The second line of code has been changed a bit. Instead of calling the getName() method on the Person instance, the method that has been inlined. That is the call to the method has been replaced by the body of the method. This is a common optimization done by compilers — historically, C/C++ and Java compilers — but is a trick that the GWT compiler takes full advantage of as well to produce fast JavaScript.

It is common in object-oriented development to abstract out a common interface that may have multiple implementations and write code that works with the interface, making it more reusable. This is another useful engineering abstraction that can impose extra lookups and method dispatch, hurting performance. Let's take a look at how the GWT compiler can help with that. You can use Eclipse's refactoring tools to easily extract an interface from the Person class in Listing 3. Let's call that a Thing. It is shown in Listing 8.

Listing 8. Refactored with Thing interface
public interface Thing {
    String getName();
}
public class Person implements Thing {
    final String firstName;
    final String lastName;
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String getName(){
        return firstName + " " + lastName;
    }
}

Now the client code that uses the Person should be changed to use the Thing interface. This is shown in Listing 9.

Listing 9. Refactored client code
Thing user = new Person("GWT", "User");
nameField.setText(user.getName());

What happens to the compiled code? Do we pay a performance price? Take a look for yourself in Listing 10.

Listing 10. Refactored code, recompiled
user = $Person(new Person(), 'GWT', 'User');
$setText_1(nameField, user.firstName + ' ' + user.lastName);

As you can see, there are no changes at all. What if we have two implementations of the interface that are both used? The code for this is shown in Listing 11.

Listing 11. Multiple implementations of Thing
public class Company implements Thing {
    private final String name;
    public Company(String name){
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}
// client code
final TextBox nameField = new TextBox();
Thing user = new Person("GWT", "User");
Thing userCompany = new Company("ACME");
nameField.setText(userCompany.getName() + " " + user.getName());

Listing 12 shows the JavaScript emitted by the GWT compiler.

Listing 12. Multiple implementations compiled away
user = $Person(new Person(), 'GWT', 'User');
userCompany = $Company(new Company(), 'ACME');
$setText_2(nameField, userCompany.name_0 + ' ' + (user.firstName + ' ' + user.lastName));

As you can see, the compiler still removes the interface and still inlines each implementation of the getName() method. This is still quite optimal code. However, there are things you can do that can prevent optimizations, as shown in Listing 13.

Listing 13. Defeating the compiler
private String mkString(Collection<Thing> things, char separator){
    StringBuilder sb = new StringBuilder();
    for (Thing thing : things){
        sb.append(thing.getName());
        sb.append(separator);
    }
    return sb.deleteCharAt(sb.length()).toString();
}
// client code
final TextBox nameField = new TextBox();
Thing user = new Person("GWT", "User");
Thing userCompany = new Company("ACME");
nameField.setText(mkString(Arrays.asList(user, userCompany), ' '));

In Listing 13, we have introduced a new abstraction: a helper function for taking a collection of Thing objects and concatenating the result of calling getName() on each Thing. The separator is also abstracted out as a parameter to this function. Now let's look at the compiled JavaScript in Listing 14.

Listing 14. Compiled mkString code
function $mkString(things, separator){
  var sb, thing, thing$iterator;
  sb = $StringBuilder(new StringBuilder());
  for (thing$iterator = $AbstractList$IteratorImpl(new AbstractList$IteratorImpl(), 
things); thing$iterator.i < thing$iterator.this$0.size_0();) {
    thing = dynamicCast($next_1(thing$iterator), 16);
    $append_2(sb, thing.getName());
    sb.impl.string += String.fromCharCode(separator);
  }
  return $deleteCharAt(sb, sb.impl.string.length).impl.string;
}
// client code
user = $Person(new Person(), 'GWT', 'User');
userCompany = $Company(new Company(), 'ACME');
$setText_1(nameField, $mkString($Arrays$ArrayList(new Arrays$ArrayList(), 
initValues(_3Lorg_developerworks_gwt_client_Thing_2_classLit, 0, 16, 
  [user, userCompany])), 32));

The code in Listing 14 is of similar complexity as the Java source code. Notice that inside the loop, a function called dynamicCast is called. This is JavaScript used to check if the object passed to it can be cast to an object of a given type. In this case, it will check if the object is a Person or a Company, as these are the only objects implementing Thing. By introducing code written against the interface and having multiple implementations of the interface, there are fewer optimizations that can be done by the GWT compiler.

So far, all of the optimizations that we have looked at were language-level optimizations made by the GWT compiler. There are other more browser-specific optimizations that can be done by GWT. These types of optimizations are generally lumped under the umbrella concept known as deferred binding.


Deferred binding

Web developers have been suffering from the variations across Web browsers ever since Mosaic was no longer the only browser around. One of the great appeals of the many JavaScript frameworks out there is to smooth over the differences between browsers to make your life a little more sane. There are two common approaches to this. First, you can write code that is portable across browsers. This is a least-common denominator approach, which is at best slightly less than optimal, but more often, it is far from optimal. The other approach is to sniff the browser and use the optimal code for each browser. This causes a lot of spaghetti code and implies that there will be a lot of code shipped to the browser that is never executed on the browser.

GWT's deferred binding architecture allows it to compile multiple versions of JavaScript for the various browsers. A small piece of JavaScript is downloaded to the browser to sniff the browser, then the optimized JavaScript is downloaded. If you look back at Figure 2, you will notice a .nocache.js file that was downloaded. This is the browser-sniffing code, and in this case, was 4 KB. By default, four permutations are checked: Opera, Safari, Gecko (Firefox V2 or less), Gecko V1.8 (Firefox V3+), Internet Explorer V6, and Internet Explorer V8.

The classic example of an API that differs greatly across browsers is setting the innerText property of an element. This is done in the starter project in the callback handler to the remote procedure call to the server. The Java code is quite simple and is shown in Listing 15.

Listing 15. Setting text in GWT
public void onSuccess(String result) {
    dialogBox.setText("Remote Procedure Call");
    serverResponseLabel.removeStyleName("serverResponseLabelError");
    serverResponseLabel.setHTML(result);
    dialogBox.center();
    closeButton.setFocus(true);
}

Now let's take a look at what the GWT compiler will emit for various browsers. To figure out what file is being compiled for each browser permutation, take a peek into the .nocache.js file and look for a section of code that looks similar to the code shown in Listing 16.

Listing 16. GWT's browser-sniffing code
if (!strongName) {
  try {
    unflattenKeylistIntoAnswers(['opera'], 
'D1B884746B9C511E12378A55F9FD97E2.cache.html');
    unflattenKeylistIntoAnswers(['safari'], 
'12DC532DA52018F17FA7F84F7137102A.cache.html');
    unflattenKeylistIntoAnswers(['gecko1_8'], 
'0986E60F243CC620FA7138AB06F221EB.cache.html');
    unflattenKeylistIntoAnswers(['gecko'], 
'CF1F7CBAF43D18B03F82260D99CB1803.cache.html');
    unflattenKeylistIntoAnswers(['ie8'], 
'1EE88964C0A866A7F2887C02F69F64D3.cache.html');
    unflattenKeylistIntoAnswers(['ie6'], 
'5395DF4A8135D37430AAE1347158CE76.cache.html');
    strongName = answers[computePropValue('user.agent')];
  }
  catch (e) {
    return;
  }
}

Now you can match up each of the keys ('opera', 'safari', etc.) to the generated files. Using this, now we can find the version of the onSuccess method from Listing 15 that was compiled for Internet Explorer V6, shown in Listing 17.

Listing 17. Compiled for Internet Explorer V6
function $onSuccess(this$static, result){
  ($clinit_11() , this$static.val$dialogBox.caption.element)
.innerText = 'Remote Procedure Call';
  setStyleName(this$static.val$serverResponseLabel.element, 
'serverResponseLabelError', false);
  this$static.val$serverResponseLabel.element.innerHTML = result || '';
  $center(this$static.val$dialogBox);
  $setFocus(this$static.val$closeButton, true);
}

For Internet Explorer V6, GWT uses the optimal API for Internet Explorer V6: using the innerText property of the element. Now let's compare this to the Gecko V1.8+ permutation shown in Listing 18.

Listing 18. Compiled for Firefox V3+
function $onSuccess(this$static, result){
  ($clinit_11() , this$static.val$dialogBox.caption.element)
.textContent = 'Remote Procedure Call';
  setStyleName(this$static.val$serverResponseLabel.element, 
'serverResponseLabelError', false);
  this$static.val$serverResponseLabel.element.innerHTML = result || '';
  $center(this$static.val$dialogBox);
  $setFocus(this$static.val$closeButton, true);
}

For newer versions of Firefox, GWT compiles the Java code into JavaScript that uses the textContent property of the element. This is a simple example, but you could easily imagine using something like innerText multiple times in your application code.

If you develop much with GWT, you quickly become aware of deferred binding — but not always for the best reason. As we have seen, GWT compiles a different version of JavaScript for each permutation of your application. These permutations are also how GWT localizes your code. By default, there are six browser variations and one language. If you needed to support three languages instead of one, there would be 18 permutations and 18 versions of the JavaScript. This can often lead to long compiles that can really cut into your productivity. Of course, one way to overcome this is to do most of your work in Hosted Mode, where you do not compile to JavaScript at all. This is by far the easiest way to debug your code. However, if you need to compile, you can greatly reduce the compile time by telling GWT to only compile for one browser (whatever browser you like to use for testing.) All you have to do is modify the module XML configuration file for your application, as shown in Listing 19.

Listing 19. Configure GWT for one browser
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN" 
"http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core
/src/gwt-module.dtd">
<module rename-to='gwtperf'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->
  <entry-point class='org.developerworks.gwt.client.GwtPerf'/>
  <set-property name="user.agent" value="gecko1_8"/>
</module>

The key here is the next-to-last line: the set-property tag. We simply set the user.agent property to gecko1_8 and now GWT will only do one compile that produces JavaScript targeted for Firefox V3+. We have now seen some of the many ways that GWT produces just the right JavaScript that will run the fastest in the user's browser.


High-speed Ajax

Ajax has become ubiquitous on the Web, and it is an essential part of any Web application. When the Ajax term was coined, the X in Ajax stood for XML. This was assumed to be the format for data being sent between browsers and servers. However, many Ajax applications actually receive HTML from the servers that they communicate with. This is usually done because it is the easiest thing to implement. It is far from optimal, though.

Some Ajax application servers send back data in the form of XML. This is a huge improvement over HTML, but it is also suboptimal. XML is a bulky format and can be awkward to parse using JavaScript. This leads to too many bytes being sent over the wire, and too much JavaScript has to be executed that the user has to wait for. Many Web applications have switched to using JSON. This is an improvement.

GWT includes both client-side and server-side components for Ajax, so you might wonder what data format GWT uses for Ajax. It probably comes as no surprise that GWT uses a unique format that is highly optimized. To take a look at it, Firebug once again comes in handy, as shown in Figure 3.

Figure 3. Monitor Ajax traffic with Firebug
Screen shot depicting monitoring Ajax traffic with Firebug

Firebug shows data sent to the server and the data returned by the server. Take a more detailed look in Listing 20.

Listing 20. GWT request and response data
Request: 
5|0|6|http://localhost:8080/gwtperf/|29F4EA1240F157649C12466F01F46F60|
org.developerworks.gwt.client.GreetingService|greetServer|java.lang.String|
IBM developerWorks|1|2|3|4|1|5|6|
Response:
//OK[1,["Hello, IBM developerWorks!<br><br>I am running Google App Engine
Development/1.2.2.<br><br>It looks like you are using:<br>Mozilla/
5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/
3.5.2"],0,5]

Of course, the Java code you would write is quite simple. GWT provides a simple programming model at design time, but a highly optimized runtime implementation.


Summary

This article has shown many of the performance optimizations GWT provides for developers and how to take advantage of the Google Plug-in for Eclipse Galileo. GWT makes it easy to write dynamic Web applications that are also high-performance Web applications. This will continue to get even better. The upcoming GWT V2.0 release includes several new features, such as code-splitting and resource bundles, that go even further toward improving the performance of Web applications created with GWT. You can get early access to these features by building the GWT trunk source code.


Download

DescriptionNameSize
Source codeos-eclipse-googlegalileo-source.zip36KB

Resources

Learn

Get products and technologies

Discuss

  • The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)
  • The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.
  • Participate in developerWorks blogs and get involved in the developerWorks community.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Web development
ArticleID=435805
ArticleTitle=High-performance Web development with Google Web Toolkit and Eclipse Galileo
publish-date=10202009