BT

High Performance Ajax with GWT

Posted by Ryan Dewsbury on Mar 04, 2008 |

Today, InfoQ publishes a sample chapter "Integrating with a GWT-RPC Servlet"  from "Google Web Toolkit", a book authored by Ryan Dewsbury.

Performance is the main reason Ajax is so popular. We often attribute the glitzy effects used in many Ajax apps as a core appeal for users, and users may also attribute this to why they prefer Ajax apps. It makes sense because if you look at traditional web apps they appear static and boring. However, if it were true that glitzy effects dramatically improved the user experience then we would see a wider use of the animated gifs. Thankfully those days are gone. Ajax will not go the way of the animated gif because the value that it adds is not all glitz. The true value that improves the user experience with Ajax, whether the user is conscious of this or not, is performance.

In this article I'm not going to show you why Ajax inherently performs better than traditional web applications. If you're not sure look at Google maps and remember older web mapping apps or compare Gmail to Hotmail. By basing your application on Ajax architecture you can dramatically improve performance and the user experience for your app. Instead, in this article I'm going to show you how to push this performance improvement to the next level - to make your Ajax application stand apart from the rest.

Why GWT?

The Google Web Toolkit (GWT) provides a significant boost to Ajax development. Any new technology like this is a hard sell especially when there are many other choices. In reality, nothing gives you the benefits GWT gives you for Ajax applications. If you're not already bound to a framework it just doesn't make sense to not use it. By using GWT for your Ajax application you get big performance gains for free.

By free I mean that you just don't need to think about it. You concentrate on writing your application logic and GWT is there to make things nice for you. You see, GWT comes with a compiler that compiles your Java code to JavaScript. If you're familiar with compiled languages (C, Java, etc.) you'll know that a goal is to make the language platform independent. The compiler is able to make optimizations to you code specific to the platform being compiled to, so you can focus on leaving your code readable and well organized. The GWT compiler does the same thing. It takes your Java code and compiles down to a few highly optimized JavaScript files, each one exclusively for use with a specific browser, making your code small and browser independent. The optimization steps employ real compiler optimizations line removing uncalled methods and inlining code essentially treating JavaScript as the assembly code of the web. The resulting code is small and fast. When the JavaScript is loaded in the browser it contains only the code needed for that browser and none of the framework bloat from unused methods. Applications built using GWT are smaller and faster than applications built directly with JavaScript and now the GWT team, typically very modest, is confident that the GWT 1.5 compiler produces JavaScript that is faster than anything anyone could code by hand. That should be enough to convince anyone to use GWT for an Ajax application but if it doesn't there are plenty of other reasons why you should use GWT including the availability of Java software engineering tools (debugging Ajax applications in Eclipse is a huge plus for me).

Do You Want More?

Why stop there. Ajax applications perform better than traditional web applications and GWT applications perform better than regular Ajax applications. So by simply making a few technology choices you can build applications that perform really, really well, and focus on your application features. You'll be done your work in half the time too. However GWT doesn't magically do everything. I will cover four things that you can do on your own to boost your Ajax application performance even further.

1. Cache Your App Forever

When you compile your GWT application to JavaScript a file is created for each browser version that has a unique name. This is your application code and can be used for distribution simply by copying it to a web server. It has built in versioning since the filename name is a hash of your code. If you change your code and compile a new filename is created. This means that either the browser has a copy of this file already loaded or it doesn't have it at all. It doesn't need to check for a modified date (HTTP's If-Modified-Since header) to see if a newer version is available. You can eliminate these unneeded browser HTTP trips. They can be fairly small but add up to a lot when your user base grows. They also slow down your client since browsers can only have two active requests to a host. Many optimizations with load time for Ajax involve reducing the number of requests to the server.

To eliminate the version requests made by the browser you need to tell your web server to send the Expires HTTP header. This header tells the browser when the content is not considered fresh again. The browser can safely not check for new versions until the expire date has passed. Setting this up in Apache is easy. You need to add the following to your .htaccess file:

<Files *.cache.*>
 	ExpiresDefault "now plus 1 year"
</Files>

This tells apache to add the expires header to one year from now for every file that matches the pattern *.cache.*. This pattern will match your GWT application files.

If you're using Tomcat directly you can add headers like this through a servlet filter. Adding a servlet filter is fairly straightforward. You need to declare the filter in your WEB_INF/web.xml file like this:

<filter>
 	<filter-name>CacheFilter</filter-name>
 	<filter-class>com.rdews.cms.filters.CacheFilter</filter-class>
</filter>
<filter-mapping>
 	<filter-name>CacheFilter</filter-name>
 	<url-pattern>/gwt/*</url-pattern>
</filter-mapping>

This tells tomcat where to look for the filter class and which files to send through the filter. In this case the pattern /gwt/* is used to select all the files in a directory named gwt. The filter class implements the doFilter method to add the Expires header. For GWT we want to add the header to each file that doesn't match *.nocache.*. The nocache file should not be cached since it contains the logic to select the current version. The following is the implementation of this filter:

public class CacheFilter implements Filter {
 	private FilterConfig filterConfig;

 	public void doFilter( ServletRequest request, ServletResponse response,
 			FilterChain filterChain) throws IOException, ServletException {

 		HttpServletRequest httpRequest = (HttpServletRequest)request;

 		String requestURI = httpRequest.getRequestURI();
 		if( !requestURI.contains(".nocache.") ){
 			long today = new Date().getTime();
 			HttpServletResponse httpResponse = (HttpServletResponse)response;
 			httpResponse.setDateHeader("Expires", today+31536000000L);
 		}
 		filterChain.doFilter(request, response);

 	}

 	public void init(FilterConfig filterConfig) throws ServletException {
 		this.filterConfig = filterConfig;
 	}

 	public void destroy() {
 		this.filterConfig = null;
 	}
}

2. Compress Your Application

The GWT compiler does a good job at reducing code size but cutting unused methods and obfuscating code to use short variable and function names, but the result is still uncompressed text. Further size improvements can be made buy gzipping the application for deployment. With gzip you can reduce your application size by up to 70%, which makes your application load quicker.

Fortunately this is an easy to do with server configuration as well. To compress files on apache simply add the following to you .htaccess file:

SetOutputFilter DEFLATE

Apache will automatically perform content negotiation with each browser and send the content compressed or not compressed depending on what the browser can support. All modern browsers support gzip compression.

If you're using Tomcat directly you can take advantage of the compression attribute on the Connector element in your server.xml file. Simply add the following attribute to turn compression on:

compression="on"

3. Bundle Your Images

Ajax application distribution leverages the distribution power of the browser and HTTP, however the browser and HTTP are not optimized for distributing Ajax applications. Ajax applications are closer to Desktop applications in their needs for deployment where traditional web applications use a shared resource distribution model. Traditional web applications rely on interactions between the browser and web server to manage all of the resources need to render a page. This management ensures that resources are shared and cached between pages ensuring that loading new pages involves as little downloading as possible. For Ajax applications resources are typically not distributed between documents and don't need to be loaded separately. However it is easy to simply use the traditional web distribution model when loading application resources, and many applications often do.

Instead, you can reduce the number of HTTP requests required to load your application by bundling your images into one file. By doing this your application loads all images with one request instead of two at a time.

As of GWT 1.4 the ImageBundle interface is supported. This feature lets you define an interface with a method for each image you'll use in your application. When the application is compiled the interface is read and the compiler combines all of the images listed into one image file, with a hash of the image contents as the file name (to take advantage of caching the file forever just like the application code). You can put any number of images in the bundle and use them in your application with the overhead of a single HTTP request.

As an example, I use the following image bundle for the basic images in a couple applications I've helped build:

public interface Images extends ImageBundle {

 	/**
 	 * @gwt.resource membersm.png
 	 */
 	AbstractImagePrototype member();
 	/**
 	 * @gwt.resource away.png
 	 */
 	AbstractImagePrototype away();

 	/**
 	 * @gwt.resource starsm.gif
 	 */
 	AbstractImagePrototype star();

 	/**
 	 * @gwt.resource turn.png
 	 */
 	AbstractImagePrototype turn();

 	/**
 	 * @gwt.resource user_add.png
 	 */
 	AbstractImagePrototype addFavorite();
}

Notice that each method has a comment annotation specifying the image file to use and a method that returns an AbstractImagePrototype. The AbstractImagePrototype has a createImage method that returns an Image widget that can be used in the application's interface. The following code illustrates how to use this image bundle:

Images images = (Images) GWT.create(Images.class);
mainPanel.add( images.turn().createImage() );

It's very simple but provides a big startup performance boost.

4. Use StyleInjector

What about CSS files and CSS images as application resources? In a traditional web distribution model these are treated as external resources, loaded and cached independently. When used in Ajax applications they involve additional HTTP requests and slow down the loading of your application. At the moment GWT doesn't provide any way around this however there is a GWT incubator project, which has some interesting GWT code that may be considered for future versions. Of particular interest is the ImmutableResourceBundle and StyleInjector.

The ImmutableResourceBundle is much like an ImageBundle but can be used for any type of resource including CSS and CSS images. It's goal is to provide an abstraction around other resources to have them handled in the most optimal way possible for the browser running the application. The following code is an example of this class used to load a CSS file and some resources:

public interface Resources extends ImmutableResourceBundle {

 	/**
 	 * @gwt.resource main.css
 	 */
 	public TextResource mainCss();

 	/**
 	 * @gwt.resource back.gif
 	 */
 	public DataResource background();

 	/**
 	 * @gwt.resource titlebar.gif
 	 */
 	public DataResource titleBar();
 	/**
 	 * @gwt.resource dialog-header.png
 	 */
 	public DataResource dialogHeader();

}

For each resource a file and method is specified much like the ImageBundle however the return value for the methods is either a DataResource or a TextResource. For the TextResource you can use its getText method to get it's contents and for the DataResource you can use getUrl to reference the data, (for example in an IMG tag or IFRAME). How this data is loaded is handled differently for different browsers and you don't need to worry about it. In most cases the data is an inline URL using the data: URL prefix. The possibilities for this class are vast, but the most immediate use is to bundle CSS directly with your application file.

Notice in the interface that a CSS file and some images are referenced. In this case the interface is being used to bundle CSS and it's images with the application to reduce HTTP calls and startup time. The CSS text specified background images for some of the application elements but instead of providing real URL's it lists placeholders. These placeholders reference other elements in the bundle, specifically the other images. For example, the main.css file has a CSS rule for the gwt-DialogBox style name:

.gwt-DialogBox{ background-image:url('%background%') repeat-x; }

To use this CSS file and it's images in your application you need to use the StyleInjector class from the GWT incubator project. The StyleInjector class takes the CSS data and matches the placeholders to resources in a resource bundle then injects the CSS into the browser for use in your application. It sounds very complicated but it's simple to use and improves performance. The following is an example of injecting CSS from a resource bundle into your application with StyleInjector:

Resources resources = (Resources)GWT.create(Resources.class);
StyleInjector.injectStylesheet( resources.mainCss().getText(), resources );

It's important to note that this technique is part of the incubator project and will most likely change in the future.

Conclusion

Ajax applications have a big usability jump from traditional web applications and GWT provides tools that give you better Ajax performance for free. You should compare the startup speed of the GWT mail sample to other sample Ajax applications. By paying attention to the deployment differences between traditional web applications and Ajax applications we can push application performance even further. I'm excited to see the next generation of Ajax applications.

About the Author

Ryan Dewsbury has been involved with C++ and Java as a developer, architect, and consultant since 1998. Initially Ryan spent several years helping build the system framework for a semiconductor manufacturing system. More recently Ryan has been working to create great user experiences through cutting edge software with a few web start-up companies. In between contracts Ryan spends time on independent software projects including Easy Message, which was acquired in 2004, and more recently Gpokr (gpokr.com) and KDice (kdice.com), two casual web-based games based on GWT.

Note:The code/text does not address security issues or error handling


Copyright: This content is excerpted from the book, "Google Web Toolkit Applications", authored by Ryan Dewsbury, published by Prentice Hall Professional, December, 2007, Copyright 2008 Pearson Education, Inc. ISBN 0321501969 For more information, please visit: www.informit.com/title/0321501969.

Hello stranger!

You need to Register an InfoQ account or to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Compression : warning with some Internet explorer version, it doesn't work by Nicolas Martignole

Just a small warning about gzip compression and the following version of Microsoft Internet Explorer: 5.x, 6.0 and 6.0 SP1. There are known bugs with gzip compression. See MSDN for more details.

You might need to add a User-Agent verification in the filter so that the filter does not compress content for some specific browser. Have a look also at the default httpd.conf for Apache, there's more information about this.

www.touilleur-express.fr

Jetty Continuations with GWT by Jan Bartel

Along the lines of performance and scalability, I thought it would be worth mentioning that GWT apps can also take advantage of Jetty Continuations:

Jetty GWT with Continuations

cheers
Jan

Re: Compression : warning with some Internet explorer version, it doesn't w by Manuel Carrasco Moñino

That's true... . But it seems the problem in IE occurs only with js compressed files, not with html. Using the default linker, GWT produces especial [...].cache.html files including the application javascript code. So you can compress the application in htmp files without problems. If you are using the gwt cross site compiler you can not use compression for these version of explorer.

Re: Compression : warning with some Internet explorer version, it doesn't w by venugopal pokala

We have developed a stand alone web based application using GWT and performance of this application in Mozilla firefox browser is 3 times better than the IE browser. Any help in improving performance of this application in IE browser would be great help to us.

Thanks,
Venu

Re: Compression : warning with some Internet explorer version, it doesn't w by totoro totoro

venugopal pokala:

What you are seeing is normal. Firefox outperforms IE on Windows quite significantly.

Apache configuration by Papick Taboada

I am using the Apache proxy in front of my tomcat to configure compression and http headers.

bit.ly/GwtApacheConfig

Comments are welcome

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

6 Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2013 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT