I’m deploying a GWT app on Amazon’s EC2 architecture behind an ELB and wanted to ensure the best experience for users while pushing out new code updates.
This is a short review of my most-likely-accurate findings. It is not well organized and basically a public mind dump vs. a private mind dump to my other team members.
I’m not going to review GWT RPC in detail, but the basic points for us are:
Serializableand is in a serialization policy file (see this faq entry for more details)
Here’s what a typical RPC request might look like:
POST https://app.com/gwtapp/dispatch Content-Type: text/x-gwt-rpc; charset=UTF-8 Origin: https://app.com Referer: https://app.com/gwtapp/361924868514EB67231B0C48DC4B136A.cache.html X-GWT-Module-Base: https://app.com/gwtapp/ X-GWT-Permutation: 361924868514EB67231B0C48DC4B136A <serialized-objects-here>
The interesting thing to notice is the
X-GWT-Permutation header–this is the strong name (hash) of the browser/locale/etc. code base the client is currently running.
When an old client sends an RPC request to a new server, we would ideally like to fulfill it as long as we haven’t changed the contract of the RPC service it’s interacting with.
GWT’s deserialization execution flow for an old request is something like:
IsSerializablecan be deserialized
For this reason, it’s important to still use the old
IsSerializable marker interface even though GWT now supports Java’s
Also, type name elision cannot be used because it also relies on the serialization policy file.
Then, assuming the RPC contract is the same, everything should still work.
If the RPC contract does change, i.e. the old client tries to use changed/removed functionality, they will get an IncompatibleRemoteServiceException which the GWT client-side code should handle and prompt the user to reload the application.
One additional wrinkle is code split points. If an old client tries to load part of the application it does not have yet, it will use the old code base name, which is no longer on the new server. The client-side
GWT.runAsync will fail with a 404 and the application should prompt the user to reload. (See AsyncSplit for how gerrit handles this.)
The same scenario above can happen here, when the client has a newer serialization policy strong name than the old server, so the old server falls back to the
However, an added wrinkle is the application bootstrapping process.
A client might:
/app.htmland get served by either a new or old server (fine so far)
/app/module.nocache.jsand get served by a new server–this is the GWT bootstrapping code and, based on the user’s browser/locale/etc. combination, it tells the browser to load
/app/new-permuation-name.cache.html, however, this request gets served by an old server that only has the old application files
404and the application bootstrapping stops
I currently know of no way to recover from this scenario. Because the error happens in between the GWT bootstrapping code and your application code, there is not a way for your application code to detect what has happened and recover.
Update: Thanks for Sripathi Krishnan for pointing out the
gwt:onLoadErrorFn function where you can prompt the user to reload their browser to recover from this scenario.
For this reason, it’s very important to have a clean switch from old to new versions–no old servers should be serving requests once new servers have come online.
Based on my current understanding, to service both old and new clients during a GWT upgrade as elegantly as possible, you should:
IsSerializableand no type name elision in the RPC DTOs
IncompatibleRemoteServiceExceptionin the RPC
Any corrections, feedback, etc., is appreciated.