TechHui

Hawaiʻi's Technology Community

Simpler and Speedier GWT with Server Side RPC Serialization

In this post I will describe how we are using server side GWT RPC serialization at Ikayzo to both improve performance and simplify client code logic.


First, let's have a quick recap of the normal startup sequence of a GWT application. As depicted below, it begins with the client requesting an HTML page containing a GWT script reference and static application text. All that is required in this initial page is a one line script that includes the GWT application's "nocache" file. The job of the nocache file is to bootstrap the application by executing the correct chunk of compiled GWT application javascript, known as the "cache" file.

The cache file can be very large, but as its name implies, it should be cached by the client and is intended to be downloaded only once. (So it is not shown in the sequence above.) The tradeoff here is that by performing a quick check with every page load GWT achieves what Google terms "perfect caching".

Once the GWT application has started there are generally two ways that it can go about gathering its required data. The typical method is to make one or more GWT RPC calls back to the server, receiving the information in callbacks. Alternately, the app can accept limited attributes from its root panel HTML div tags or use JSNI calls to read arbitrary JavaScript variables from the page and browser environment.

Using GWT RPC is the canonical way to get data into a GWT app and generally yields stronger code because it stays in the realm of Java's static typing. (Presumably if you are using GWT you already understand these benefits, so I won't harp on this point.) However the alternative of simply reaching out into the page through JavaScript and grabbing what you need has a strong appeal. First, it is often easier to add some data to the HTML page and pluck it out on the client than to implement a new service method and handle the callback in your application code. Second, there is the obvious performance advantage of having the data ready to go versus making yet another call back to the server. The performance benefit can be especially poignant in this case because it's the first thing the user experiences when they load the page and wait for the app to do something useful.

Fortunately, there is a way to get the both benefits of GWT RPC's static typing and the performance gain and simplified logic of "ready to go" data stored in the page. The answer is to make one or more GWT RPC calls on the server side and serialize the results into the page as you would other javascript data. The client side GWT code will then pull them out of the page and deserialize the GWT object, just as if it had been received from the callback, enjoying all of the type safety without the round trip and asynchronous callback logic.

We used this technique extensively in a recent project and it has provided a lot of benefit for both the end user experience and maintenance of the client code. There are, however, tradeoffs in complexity on the server side and a few things that are a real hassle to deal with. In the rest of this post I'll share the snippets of code that will save you some time and outline some of the issues that still need to be addressed to make this easier in the future.

In our code we have created a number of convenience methods and built some of this into our base application layer. But the effect is that on the client side we can simply make a call like the following in our widget initialization code:
// Client Side - What we're going for...
Customer customer = getSerializedObject("customer");
The first step to making the above happen is to capture the Customer object on the server side while we are building the page. This implies of course that you have a presentation layer or servlet feeding your pages and that it knows what the client needs. We use Struts 2 as a "glue" layer to help us build our GWT pages and so in our case this server side data capture happens in a Struts action class.

Let's assume that we get our Customer from a GWT RPC service with a method called getCustomerById(). First we'll simply invoke the method on the server side. If you haven't done anything egregious in your service implementation then instantiating an instance of it should be a lightweight operation. (We use Spring to hold all of our controllers and setup). Alternately you can register your instance somewhere and reuse it. So we'll get the Customer object like this:
// Server Side - Get the customer object
long customerId = ...;
Customer customer = new MyServiceImpl().getCustomerById( customerId );
The next step is to serialize the object.
// Server Side - Serialize customer to a String
// ...
String serializedCustomer =
RPC.encodeResponseForSuccess( serviceMethod, customer, serializationPolicy );
The snippet above references our customer object as well as two new items that require explanation: The service method and serialization policy. The public API for serializing the object is the static method RPC.encodeResponseForSuccess(). This method requires a reference to the java.lang.reflect Method object representing our getCustomerById() interface method. It's not going to invoke the method (we've already done that), but it seems to require it simply to determine the declared return type. We can get the method reference like this:
// Get the Method for encodeResponseForSuccess()
Method serviceMethod = MyService.class.getMethod( "getCustomerById", Long.TYPE )
Here we are asking the MyService class for a reference to our method. The first argument to getMethod() is the name of the method and what follows is a varargs list of parameter types. In this case the special field Long.TYPE represents the primitive long type. (Long.class would represent the Long wrapper type). If there were additional arguments we'd list their types here.

Next there is the serialization policy. This is a bit of a headache. If you wish to simply tag your domain classes with java.io.Serializable (standard Java) and use them as is recommended with GWT going forward then you have to load a serialization policy that tells GWT which classes it is allowed to serialize. The serialization policy lists all of the application classes that you reference from the interface. GWT generates one of these files for each of your service interface at build time. The file is named .gwt.rpc. The is the 32 character MD5 hash that is used to uniquely name files in the GWT build. You must identify this file and load it using the SerializationPolicyLoader. Here we have copied the file to the root of our classpath and renamed it simply "gwt.rpc". Please note that this file must be deployed to the server side classpath in this scenario:
// Server side - Get the SerializationPolicy for encodeResponseForSuccess()
// Note: You should cache the SerializationPolicyInputStream in =
GadgetAction.class.getResourceAsStream( "/gwt.rpc" );
SerializationPolicy serializationPolicy =
SerializationPolicyLoader.loadFromStream( in );
The difficulty I alluded to can be seen above. I cannot tell you how to identify the correct .gwt.rpc file as it will have a unique name based on the contents. At the moment this is a manual step in our build when the interface changes. I can think of a number of hacky ways to automate this step, but as far as I know there are only two good solutions (neither of which I have tried yet). One would be to write a GWT linker that participates in the build and can easily export a copy of the file under the correct name. I'm sure someone will respond to this posting by telling me how easy that is to do and I hope they also send an example :) Another way would be to simply implement a custom SerializationPolicy that is aware our class structure in a broader way. But such as custom policy would have to be aware of which standard types are allowed to be serialized as well as which custom types. Getting this wrong would not be a security issue in this case, but would certainly cause failures at runtime.

Ok, the tricky part is over and now we have our serialized customer object as a string on the server side. The next step is to embed it into a javascript variable in the page. To do that we're just going to insert a javascript block and declare a variable, customer, with a single quoted string value of our serialized customer object. We use a Struts 2 expression to do this, but you could easily insert this with raw string replacement in a servlet or using any template tool:

Before doing that substitution though, there is one last thing you must do. Your serialized data may contain characters that would be misinterpreted inside a single quoted javascript string. This is especially true if your data contains text with markup or HTML. To sanitize it we created the following helper method, escapeForSingleQuotedJavaScriptString():
// Server Side String utils
public static String escapeForSingleQuotedJavaScriptString( String s )
{
s = escapeScriptTags( s ); //

Views: 13856

Comment

You need to be a member of TechHui to add comments!

Join TechHui

Comment by Dan Moore on November 9, 2010 at 10:29am
Hmmm.. I was just passed this article by a blog reader. Very interesting ideas. Can I ask why you didn't just use JSON and javascript overlays (which gets you the static typing)? Was it because of the size of the JSON?
Comment by Konstantin A Lukin on October 12, 2009 at 2:43pm
Thank you, Pat, for this in-depth explanation along with code examples. We've adopted this technique for serving static Java data content.

FYI, here are sample size numbers, encoding same object in different formats (in # of chars)
Xml 211248
Json 38107
Rpc 14562

A winning 2.6 compression ratio was achieved by doing RPC vs Json, and 14.5 ratio by RPC vs Xml.
(Actual numbers depend on length of field names, but is still a pretty good win :)

Js files containing serialized data are downloaded automatically, which is great! Also, no client-side libraries needed to decode RPC, which is another big plus.

Overall, a considerable improvement in loading time, since app no longer has to do round-trip RPC for static content. So once again, thanks for posting!
Comment by Dobes Vandermeer on February 16, 2009 at 10:46pm
I liked this idea so much I ran with it and published the source to my open source reusable GWT stuff dumping ground: http://code.google.com/p/kiyaa/

My version is based on a combination of GWT's Dictionary (for accessing javascript objects) and Constants (type safe interface) and a bunch of RPC code I scooped from the GWT codebase. The result is a nice little type-safe system for serializing the objects on the server and de-serializing on the client. I wrote an extremely brief description of how this works over here:

http://www.dobesland.com/2009/02/17/faster-gwt-startup-with-pre-loaded-objects/

If I get some queries and interest I might be motivated to flesh out the docs a bit more and provide some advice ... let me know, you can reach me via the Kiyaa google group or comment on the blog ...
Comment by Cameron Souza on February 6, 2009 at 3:50pm
Great article. Thx!
Comment by Pat Niemeyer on February 6, 2009 at 4:57am
Could you, as part of your build process, have a small file that imports all of your custom types and then do a depth-first traversal starting from "Object" and adds to a list anything passing the "instanceof Serializable" test.

Nate, it would be easy enough to inspect your interface using reflection and traverse up the object hierarchy to determine the classes involved. And this is what GWT is doing effectively during the build step. GWT determines the graph of classes used and notes which ones are instantiable (i.e. not abstract, have an accessible constructor). The SerializationPolicy doesn't actually need a list of classes though, it just has a pair of methods that are queried to ask "is this ok to serialize?", and "are these fields ok to serialize?" The first is used to test the object and the second is used to determine how far up the object's superclasses fields should be included.

What I meant with respect to creating a custom policy was that we might simply say "ok" to any class in our application packages. e.g. "com.foo.xxx". This sounds simple enough, but I'm not sure of the best way to get the default behavior for all of the core classes. The StandardSerializationPolicy that is used when you load a policy from a file as I've shown gets these base classes from the file.. they are not automatically known. I could probably just make my own list since the range of core types that I pass in RPC methods does not grow that fast... But it just seems like there should be some easy way to get the correct behavior here.
Comment by Daniel Leuck on February 6, 2009 at 3:38am
You can traverse superclasses, but not subclasses because there is no way to discover them. For example, tools like BeanShell make it trivial to create subclasses at runtime.
Comment by Nate Sanders on February 6, 2009 at 2:44am
...and upon digging a little deeper, it appears that "Class" has no method for grabbing its subclasses.
Comment by Daniel Leuck on February 4, 2009 at 12:01pm
Its nice to see a meaty GWT post on TechHui! For those of you that haven't tried GWT, we highly recommend you give it a spin. GWT innovates on many levels from its RPC and unique compiler to the desktop-like UI framework. It really is a game changer. GWT has enabled us to build web UIs that simply wouldn't have been possible, at least not within a reasonable time frame, with any of the frameworks we've used previously in the Java, .NET and Ruby realms. The ability to have all of your code running in a mature Java IDE with a robust debugger, code assist, etc. is a godsend.

A few months back we were faced with the task of writing a client side component that can parse a mixture of HTML and Wiki syntax to provide WYSIWYG / code view round tripping without going to the server. This would have been incredibly painful with Javascript. Thanks to GWT's Java -> Javascript compiler I was able to do the whole thing in Java in a couple days. If I was forced to use Javascript for a non-trivial parser I probably would have ended up with my head planted firmly in my monitor.
Comment by Nate Sanders on February 4, 2009 at 11:07am
Another way would be to simply implement a custom SerializationPolicy that is aware our class structure in a broader way. But such as custom policy would have to be aware of which standard types are allowed to be serialized as well as which custom types.

I rarely program in Java and it's been a while since I did, so what I'm about to suggest might have serious problems. Could you, as part of your build process, have a small file that imports all of your custom types and then do a depth-first traversal starting from "Object" and adds to a list anything passing the "instanceof Serializable" test. Produce a file containing these (text/serialized ArrayList/whatever) and then have your SerializationPolicy load this information at instantiation.

I'm not sure when Java builds its class hierarchy, which obviously matters a lot -- without instantiating any of your custom objects, maybe some of those import statements are going to be ignored.

In any case, good article.

Sponsors

web design, web development, localization

© 2024   Created by Daniel Leuck.   Powered by

Badges  |  Report an Issue  |  Terms of Service