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).
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.
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
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
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.
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.
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
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.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code | os-eclipse-googlegalileo-source.zip | 36KB | HTTP |
Information about download methods
Learn
-
Check out the best place for GWT information: Google Web Toolkit site.
-
Read one of the first
in-depth tutorials on GWT: "Ajax for Java
Developers: Exploring the Google Web Toolkit."
-
Read "Build an
Ajax application using Google Web Toolkit, Apache Derby, and Eclipse" to learn more about building
Web applications with GWT.
-
See "Build an Ajax-enabled
application using the Google Web Toolkit and Apache Geronimo" to get familiar with GWT's Ajax
capabilities.
-
"The busy Java
developer's guide to Scala: Functional programming for the object oriented" explores more functional
programming on the JVM using the Scala programming.
-
Check out the "Recommended Eclipse reading list."
-
Browse all the Eclipse content on developerWorks.
-
Follow developerWorks on Twitter.
-
New to Eclipse? Read the developerWorks article "Get started with the Eclipse Platform" to learn its origin and architecture, and how to extend Eclipse with plug-ins.
-
Expand your Eclipse skills by checking out IBM developerWorks' Eclipse project resources.
-
To listen to interesting interviews and discussions for software developers, check out check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products, as well as our most popular articles and tutorials.
Get products and technologies
-
Download the latest version of the Google Web Toolkit, GWT V1.7.
-
Get the Firebug plug-in. V1.4.2 was used in this article.
-
Obtain the latest Eclipse IDE. Eclipse
V3.5 was used in this article.
-
Get the Google plug-in for Eclipse.
-
Check out the latest Eclipse technology downloads at IBM alphaWorks.
-
Download Eclipse Platform and other projects from the Eclipse Foundation.
- Download
IBM product evaluation versions
or explore
the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
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 (Undergoing maintenance)






