Tuesday, November 28, 2006

Deploying a GWT app in an app server

I have been playing around with the Google Web Toolkit, and it’s very impressive. If you haven’t looked at it, it is a way to build AJAX web pages without (theoretically) having to worry about the JavaScript. You write your application in Java, like a Swing app, with layouts and GUI components. GWT comes with a compiler that converts your client-side Java code to JavaScript. Google has apparently worked out all of the browser differences, based on their experience with apps such as Gmail and Google Maps.

One of the coolest features is that you can debug your app in “hosted mode”, which runs the client in a Java VM. In this way you can debug your client in Java code before deploying it as JavaScript to an app server.

When it’s deployed in an app server, your client code will make Google-specific RPC calls to your server code. You write your server code in servlets (that extend a Google servlet class).

There are a couple of things I would worry about before picking GWT for a production app. One is that if you want to change the layout of a page, you have to make a change to your Java code. If you have an organization where web-design specialists are the ones who make changes to the HTML, say by modifying a JSP, GWT will break this separation of roles. You can avoid a lot of this by using stylesheets (GWT encourages this), but perhaps not completely.

The other thing to worry about is how sound the Java-to-JavaScript compiler is. I haven’t used GWT to get a good sense of this yet. But, imagine how much harder it would be to debug GWT-generated JavaScript code than it is to debug your own code.

Anyway, the main purpose for writing this blog entry was that Google has not adequately addressed the following question: How do you integrate a page using GWT into a larger web application? Specifically, how to bundle all the required files into a war?

I tried to find this answer on Google’s site, and couldn’t. Searching the rest of the web turned up a couple of really complex ant files that claimed to create a war containing a GWT app, but I wanted something simple. This article by Gautam Shah helped a lot, and helped me distill the required files down to this:

• A manifest.mf file, in the META-INF directory. This is a requirement for creating a war in general, and the manifest doesn’t need to contain anything specific to GWT.
• A web.xml file, of course, in the WEB-INF directory. The only GWT-related stuff that must go in here is your servlet declarations. See below for my web.xml file.
• The classes created when you compile your client and server-side Java code, in the WEB-INF/classes directory.
• The gwt-user jar, in the WEB-INF/lib directory. There are two big caveats here. First, make sure you include the same jar that you compiled against in the previous step; the protocol can change between versions of the jar. Second, and this is a HUGE gotcha, you must remove the javax.* classes from this jar before including it in your war. The reason for this is that the jar contains javax/servlet/Servlet.class, which prevents app servers from loading the jar, according to the Servlet spec. The jar contains these classes because they are required for running in hosted mode.
• Other files created when you run the Java-to-JavaScript compiler. These should go in the top level of the war.

Here is my web.xml file. I have one GWT service, deployed as a servlet called “counter”:


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

<display-name>GWT Test</display-name>

<servlet>
<servlet-name>counter</servlet-name>
<servlet-class>
com.kmlsolutions.gwttest.server.CounterServiceImpl
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>counter</servlet-name>
<url-pattern>/counter</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>TestApp.html</welcome-file>
</welcome-file-list>
</web-app>


And here is my ant build.xml file, where gwt-user-wo-javax.jar is just the gwt-user.jar that comes with the gwt, but with all the javax.* classes removed:


<project name="gwttest" default="war" basedir=".">
<target name="war">
<war
destfile="gwttest.war"
webxml="conf/web.xml"
>
<lib file="gwt-user-wo-javax.jar">
<classes dir="bin" excludes="**/MANIFEST.MF">
<fileset dir="www/com.kmlsolutions.gwttest.TestApp">
</war>
</target>
</project>

In theory, you should be able to add more servlets, jsps, images, and other web artifacts to this war without affecting the GWT page(s); that's the next thing for me to try. I tested this app in Tomcat 5.5, so I don't know yet if these steps are adequate for other app servers.