Sunday, February 18, 2007

GWT-RPC: Customizing RPC in GWT

In the unlikely event that you were not aware, the GWT developers are hard at work on a 1.4 release. This release, like many software projects, is going to miss it's release date, but now that GWT is open source we can get a peek at the code that is coming our way soon. In this article I want to take a look at the new and improved RemoteServiceServlet. If you simple use GWT-RPC, then the change will likely be unnoticeable, as it should, but if you have been struggling to customize GWT-RPC, then the changes are exactly what the doctor ordered.

Who, What, and Why?

Before we get into the changes, I would like provide some history for this change so that we can better understand why this change is so important. Back on November 10th, George Georgovassilis posted bug report 389, which suggested changes for making the GWT-RPC mechanism easier to extend. If you aren't familiar with George's work, he is responsible for the GWTSpringController from the GWT-SL project, allowing a tight integration of GWT and Spring. The problem is that the current RemoteServiceServlet is nearly impossible to extend in a clean way, so George ended up needing to use CGLib, a code generation library that allows you to create classes at runtime. George's code does the trick, but it should be a lot easier to extend the RemoteService Servlet.

This bug report received very little attention until this post on the GWT Contributors list from Rob Jellinghaus.

Date: Fri, Jan 5 2007 10:23 pm
From: "Rob Jellinghaus"

Issue 389
http://code.google.com/p/google-web-toolkit/issues/detail?id=389 is listed as "low" priority, assigned to gwt.team.mmendez.

I am interested in working on this in order to facilitate a better GWT-to-JSF coupling. (Basically I want to be able to specify a JSF managed bean as the service endpoint for a GWT component via JSF, without needing to write a per-bean RemoteServiceServlet, or do CGLIB magic as in http://g.georgovassilis.googlepages.com/usingthegwthandler. Just checked out the GWT source with an eye towards making a patch to support this.

So my question is mainly for mmendez: is any active development underway on this that I am likely to collide with, or is this pretty much going to stay on the back burner for the next month or two? It'd be dispiriting to get it working only to find out someone else already got it into the latest release candidate :-)

Cheers!
Rob Jellinghaus

This got the ball rolling, and discussion ensued. Rob Jellinghaus did a bit more than just discuss the problem, he also coded the solution. So many thanks goes to Rob Jellinghaus for this patch that we should be seeing in the 1.4 release of GWT. So, not that we know the who, what, and why, lets take a look at the how.

RemoteServiceServlet - The OLD Way

As you already know, when your GWT code on the client calls the server, your custom servlet is executed, and your servlet extends the GWT RemoteServiceServlet. The execution starts with the doPost() method of the servlet being called, which in turn takes the serialized RPC request and passes it to processCall() for processing. The sequence diagram below shows the processing flow.



Inside of the processCall() method it does literally all of the work for handling the request. It passes the serialized request to the onBeforeRequestDeserialization(), allowing a subclass to perform some work on the serialized data prior to deserialization. It then checks that the target class implements the RemoteServiceInterface, deserializes the payload, invokes the RPC request, serializes the response, and finally calls onAfterResponseSerialized(). In short, the processCall() method does a LOT of work, allowing only a peek at the data before deserialization and after serialization of the response. It does not allow you to alter the process in any way unless you override and implement the entire processCall() method.

RemoteServiceServlet - The NEW Way

The changes to the RemoteServiceServlet in GWT 1.4 won't change the way you use GWT-RPC, but it does add a lot opportunities for customizing the handling of the RPC request. The sequence diagram below shows part of the picture. The two things that stand our are that the onBeforeRequestDeserialized() and onAfterResponseSerialized() methods are now outside of the processCall() method, and that there is now a new RPC class.




By moving the two "peek" methods out of processCall(), and moving all of the logic into a new RPC class simplifies the processCall() greatly. So great is this reduction that processCall() only consists of two lines of code! Below is the new processCallMethod().


public String processCall(String payload)
throws SerializationException {
RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass());
return RPC.invoke(this, rpcRequest.getMethod(), rpcRequest.getParameters());
}


There are a few key points about this new mechanism.

1. All of the logic has been moved into public static methods of the RPC class. This makes it possible to write your own service that doesn't use the RemoteServiceServlet at all.

2. None of the methods in RPC access the HttpServletRequest or HttpServletResponse object. Besides simplifying testing, you could in theory write a service that doesn't even use a servlet container. In theory you could write a service that works over email, FTP, telnet, or pretty much anything. Any volunteers to be the first to try sending GWT-RPC messages over email?

3. The processCall() method is only two lines of code. This allows you to override the processCall() method without the hacking that was required previously.

Now lets take a closer look at the methods in the RPC class.

The New RPC Class

There are only a few public methods in the new RPC class, so we might as well look at them all.

public static RPCRequest decodeRequest(String payload, Class service)

You saw this method above in the processCall() method. You pass in the serialized RPC request, and the class that will accept the call. As a security precaution, the method will check that the service class implements the expected service interface as well as the RemoteService interface. The method then deserializes the request and returns a RPCRequest object. The RPCRequest class is new, and contains a Method object and and Object array of parameters. The Method class is part of Java's reflection API, and this object can be used to invoke the method.

public static RPCRequest decodeRequest(String payload)

This is the same as the first version of decodeRequest(), except that it will skip the check to see if the target service class implements the proper interface. Internally, calling this method is the same as calling decodeRequest(payload, null).

public static String invoke(Object target, Method serviceMethod, Object[] parameters)

The invoke method executes the method and returned a serialized response. The target is the object that the method will be called on. In the default processCall() method, that we saw in the code snippet above, the "this" object is passed, meaning that the servlet itself must implement the method. But because you can pass the object to the invoke method you could delegate the RPC call to some other class. This could be useful in frameworks like Spring, where you want to use dependency injection to allow for swapping out implementations. The serviceMethod parameter is the Method object that will be called, and parameters is an array of Objects that will be passed to the method.

public static String encodeSuccess(Method serviceMethod, Object returnValue)

The encodeSuccess() method is used internally by the invoke() method that we just discussed. Simply put, it takes the Method object that was invoked, and the Object result of the invocation, and serializes the result. Because this method is broken out of the invoke() method it allows you to replace the invoke() method with your own, and still be able to use the encoding facilities.

public static String encodeFailure(Method serviceMethod, Throwable cause)

Again, the envodeFailure() method is used by invoke() above, but if you need to write your own invoke() method you can still use this method to serialize an error.

The Sum of the Changes

As you can see, the changes are completely backwards compatible, yet allow for ease of extension. You can customize the handling of the processing of requests by overriding the processCall() method to add code to alter the serialized response before processing, modifying the serialized response before it goes back to the client, alter the way the method is invoked, and even delegate execution to some other class. I look forward to seeing further GWT integration on the back-end with Spring, JSF, EJB, and everything in between.

GWT ClippedImage - Optimizing Image Loading

If you have been watching the news groups you will have heard about the upcoming GWT 1.4 release, and the long list of features it will include. One of these features is the ClippedImage class. This article is meant to explain how to use ClippedImage, and the details of how it can optimize image loading in your GWT application.

What is ClippedImage, and what is it for?

ClippedImage allows you to show just a part of an image instead of the whole image, by hiding or "clipping", the rest. The ultimate purpose of this new image type is for optimizing the load time of your GWT appliciation. Curious as to why? Then read on...

First some background information...

We use images in web pages for icons, logos, art, photos, and to make the user-interface look beautiful. There is no escaping it, we like to use lots of images! As you might guess, the flip side of the coin is that lots of images means longer download times, but perhaps not for the reason you think. Of course the larger the image the longer the download time, but what about the overhead of the HTTP protocol? Due to the way browsers and HTTP works, there is a bit of overhead for each image downloaded, and although it is rather small, it can quickly add up if you have a lot of images.

As an example may I present a sample icon bar. The bar includes a set of 14 icons (12 shown in the image), each 22 pixels by 22 pixels, with the total size of all of the images a mere 15 kB.



To see how well the browser performs when loading these images I created a simple HTML page that included 14 image tags, one for each icon. I then put the images and the test HTML page on a web site and viewed it in the browser. I use the FireBug plug-in (a must have!) for Firefox, and it shows me that the total time to download the page and all of the images is a mere 610 milliseconds. In FireBug you can get a clue as to what is going on, the images don't all download at the same time, and as we will see, this is by design.



In section 8.1.4 of the HTTP 1.1 specification it states,
"Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy."
I think that this makes it clear why we see really much longer times for some of the images on the page even though they are roughly the same size. So if the problem is the connection limit that HTTP specifies, what is the solution? Considering this article is about the new ClippedImage class, I will bet you know what comes next. The solution is to reduce the number of external images that we need to load by combining all of out icon files into a single file.

In order to test this I created a single PNG image that contained all of the 14 icons, and reran the test. Below is the FireBug output for the single image, which is the same size as the 14 icons combined.



The results are pretty amazing, with a page load time of roughly 38% of the of the original. In our example the difference is less than 400 milliseconds, but if we needed to load more icons this could easily add several seconds to the time it takes for the page to load, which can be significant depending on the application.

Now that we have some background information, lets get on to using ClippedImage to increase load time.

Using ClippedImage

Using clipped image is very simple. You simply specify the X/Y coordinates of the top-left pixel of the part of the image where you want the clipping to occur, followed by the width and height of the image. The following code snipped shows the creation of four clipped images.

ClippedImage unlkIcon = new ClippedImage("icons.png", 0, 0, 22, 22);
ClippedImage timeIcon = new ClippedImage("icons.png", 22, 0, 22, 22);
ClippedImage warnIcon = new ClippedImage("icons.png", 44, 0, 22, 22);
ClippedImage recyIcon = new ClippedImage("icons.png", 66, 0, 22, 22);


In the code snippet we highlighted, we create a warning icon. The diagram below shows where the coordinate measurements come from. The top corner of the icon is 44 pixels from the left edge, and zero pixels from the top. The next two arguments are the width and height, both are 22 pixels.




Oh, and you might have noticed that we used a PNG in the example. It is a known issue that Internet Explorer 6 doesn't support the PNG alpha channel without having to jump though some hoops. The PNGImage class from the GWT Widget Library jumps through these hoops, and it is rumored that the new ClippedImage class will also support this.

In order to add ClippedImage into the GWT API there were some other modifications made. If you think about it, now that there is the old Image class, and the new ClippedImage class, it only made sense to add a parent class to hold shared functionality. This new class is called AbstractImage, and you guessed it, it is abstract. This super class includes mouse event handling, simplifying any subclasses a little.

Automatic bundling of images

Along with ClippedImage is the new ImageBundle. I won't cover ImageBundle, but if you want to read up on it, there is a ImageBundle design document. The ImageBundle is similar to the GWT internationalization support, except that it is for images. The idea is that you create an extention of the ImageBundle interface, with one method specified for each image, and each image being its own separate file. When you compile the GWT code the GWT complier will automatically grab all of the individual images and combine them into a single image. In the GWT code you then use the methods of the interface to get the image objects. The whole purpose is to make image clipping easy to do.

I hope you enjoyed this article. I am not sure what I might write about next as there are a lot of cool new things in GWT 1.4. The complete list of new features can be found in the GWT 1.4 Development Plan.

Attribution: The icons used in the sample images are from the Tango Icon Library.