Tuesday, March 10, 2009

GWT and Google App Engine - An Update

In my previous post GWT and Google App Engine - How to Develop! I outlined an approach one could take to develop a GWT application using the Google App Engine (either from the outset or partially mapping services to GAE over time). In the article I recommended the usage of the HttpRedirectFilter. While this filter worked well using the GAE SDK, I ran into problems connecting to google's remote hosts. In particular, the filter was returning 302 status codes (Temporarily Moved) on the redirect, which AJAX will not treat as a successful outcome (AJAX requires 200-series status codes for successful responses). Instead, I decided to simply write my own filter based on similiar design outlined in the book "GWT Web Toolkit Applications" written by Ryan Dewsbury (ISBN-13: 978-0-321-50196-7). While this book is written against GWT 1.4 (and prior) it is a great reference for developing applications for GWT. There may be newer editions of the book and I would recommend it it.

If there is interest in the filter, I suppose I could post it to the blog or a source repository as open-source under Apache 2.0 License. I did run into a few tricks that go beyond what is highlighted in the GWT book which are worth noting (some are specific to forwarding to GAE, some are general "advanced" WEB related)
  1. GZIP encoding. If the response from the host (GAE or other) has a Content-Encoding header set containing gzip you will need to use a GZIPInputStream( ) to parse the input or error streams from the response. By default, the GAE SDK was not returning GZIP output, and my results were fine, until I ported my python app to GAE servers. At this point the response came back in gzip thus resulting in garbage data without the GZIPInputStream.
  2. The proxy as defined in the book proxies from Source A to Host B. In my case, I wanted some more advanced functionality (like mapping partial or complete URIs to a new URL) so implemented a simple redirectMappings.xml property file to do this.
  3. For example, in a Restful Web application, you might have a resource path like /resources/preferences/categories/currency where the resource is preferences and the remaining URI values are directing the request (in this case getting all preferences with the currency category). Ideally you do not want your mapping to be setup against all possible URIs, but only the resource path (/resources/preferences in this case) appending the remaining URI path to this redirected URL.
  4. The example proxy does not encode the form data in the proxy (and assumes you are encoding it in your client under a "post" attribute). My approach was to treat the requests from GWT no different than any application request, so any values like method, FORM data should be encoded into the headers/parameters rather than having the proxy except some specialized structure of data. This makes the proxy "benign" in nature and it also means that it does not really matter if the proxy is there or not (from the client side).
  5. FORM data should only be added if the request is a POST, PUT or DELETE request. (although in REST, the DELETE should be a path variables on the URI) GAE will return a status code of 500 if the GET request contains FORM data.
  6. GAE will reject requests which contain certain headers. I have had difficulty getting information from the Google site on this, but from the discussion forums (as well as my testing) requests which contain the following were rejected:
    • DATE
    • HOST
    • VARY
    • VIA
    Again, this is an unconfirmed list. This list was pulled from two discussion threads against issues filed against GAE (Issues 198 and 342).

I also thought I should make a brief commentary on the overhead associated with using a servlet filter for redirects. It would appear that using the filter imposes ~ 400-500 ms overhead per request. This is probably not a big deal on development systems, but obviously I would not recommend this for production. My suggestive approach in this article (and former) are really meant to be used for a developer to build an application concurrently with GWT (using the Hosted Mode browser) in conjunction with the GAE SDK (or even hosted server(s)). This eliviates the pain of having to have a fully functioning service/server prior to developing the client and allows each to be developed concurrently making a much cleaner development experience (especially if you are like me and doing this as a hobby).

No comments: