GWT And Maven

There's no doubt that the Google Web Tookit is one of the most interesting frameworks in the Java world for developing AJAX web applications.

But no single library or framework is very useful on its own; any non-trivial Java web application these days easily integrates tens of different libraries. This is why many projects rely on a project build and management tool such as Maven 2 to provide a standard project layout, dependency management, build and release process etc.

Now the problem is, GWT is not a regular Java library that you can simply add to your classpath. GWT is special in quite a few ways that make it more difficult to integrate into your project:

  • GWT compiles Java to JavaScript, so you need an extra step in your build process
  • This JavaScript compilation step also applies to modules you include as a dependency; jars do not include generated JavaScript
  • Tests (extending GWTTestCase) must be run with a special test runner (the GWT JUnitShell)
  • The same tests need access to your Java source files, not just compiled classes
  • The GWTShell (very useful during development) relies on native code so you need different packages for Linux, Mac, and Windows
  • GWT 1.4 does not support Java 5 features, but most projects still want to generics, autoboxing, etc. in regular (non-GWT) source files so ideally you need a way to separate the two (at least until GWT 1.5 is out)

Probably because of these difficulties, there are currently a few different GWT Maven plugins around (gwt-maven.googlecode.com, mojo.codehaus.org/gwt-maven-plugin, gwtforge.com) but none of them looks really solid.

The one at gwt-maven.googlecode.com, currently at version 2.0-beta18, is easily the best of the pack. The project documentation is still incomplete, but luckily there is a sample project that is very useful as a reference.

Here's how I managed to get a simple project to build, including the ability to run tests and to run the UI in the GWTShell hosted mode, both of which I think are absolutely necessary for development.

The first step is to add a new repository to our pom.xml for both dependencies as plugins:

<repositories>
  <repository>
    <id>gwt-maven</id>
    <url>http://gwt-maven.googlecode.com/svn/trunk/mavenrepo/</url>
  </repository>
</repositories>
<pluginRepositories>
  <pluginRepository>
    <id>gwt-maven</id>
    <url>http://gwt-maven.googlecode.com/svn/trunk/mavenrepo/</url>
  </pluginRepository>
</pluginRepositories>

Then we need to make a choice between using an existing GWT installation, or having the plugin download and install the GWT package automatically. I prefer to use my own installation, so I need to define a google.webtoolkit.home property that is used by the plugin. I also define a few other properties for convenience:

<properties>
  <gwt.version>1.4.62</gwt.version>
  <gwt.home>${user.home}/apps/gwt-${gwt.version}</gwt.home>
  <gwt.platform>linux</gwt.platform>
  <google.webtoolkit.home>${gwt.home}</google.webtoolkit.home>
</properties>

(Values for gwt.platform and gwt.home would be best defined in the user's settings.xml since they are machine-specific rather than project-specific.)

I can now add the GWT dependencies

<dependency>
  <groupId>com.google.gwt</groupId>
  <artifactId>gwt-user</artifactId>
  <version>${gwt.version}</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.google.gwt</groupId>
  <artifactId>gwt-servlet</artifactId>
  <version>${gwt.version}</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.google.gwt</groupId>
  <artifactId>gwt-dev</artifactId>
  <version>${gwt.version}</version>
  <scope>system</scope>
  <systemPath>${gwt.home}/gwt-dev-${gwt.platform}.jar</systemPath>
</dependency>

Note that gwt-user and gwt-servlet are regular jars and they will be downloaded from the newly added repository, while gwt-dev depends on native libraries so I point it to my local GWT installation.

Next is adding the plugin configuration to the build section of the pom.xml

<plugin>
  <groupId>com.totsp.gwt</groupId>
  <artifactId>maven-googlewebtoolkit2-plugin</artifactId>
  <version>2.0-beta18</version>
  <configuration>
    <logLevel>INFO</logLevel>
    <style>PRETTY</style>
    <compileTargets>
      <param>samples.gwtmaven.GwtMavenSample</param>
    </compileTargets>
    <runTarget>samples.gwtmaven.GwtMavenSample/index.html</runTarget>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
        <goal>gwt</goal>
        <goal>testGwt</goal>
      </goals>
    </execution>
  </executions>
</plugin>

With this configuration in place, we can create a GWT module samples.gwtmaven.GwtMavenSample composed of the usual pieces:

  1. the samples/gwtmaven/GwtMavenSample.gwt.xml module declaration in src/main/resources
  2. the samples/gwtmaven/public/index.html host page in src/main/resources
  3. the samples.gwtmaven.client.GwtMavenSample EntryPoint class in src/main/java

Running mvn package will now include the Java to JavaScript compilation step. Running mvn gwt:gwt will run the UI in GWT hosted mode, where any changes to classes are picked up dynamically.

Now the tricky thing is how to run tests that extend GWTTestCase. Simply creating a GwtMavenSampleTest will not work, because Surefire (the default Maven test runner) will fail when trying to run it. After some pain, I discovered that in fact your GWT test names must not follow the usual *Test pattern, but rather a GwtTest* one. This way they are not run by the standard Maven test runner, but by the special testGwt goal provided by the plugin.

So we need to call our test GwtTestGwtMavenSample, in src/main/test. After this, it will run as part of the usual lifecycle when doing e.g mvn test or mvn package.

Note that unit tests that do not require extending GWTTestCase can be named and run in the usual way, it's just the ones that require the special GWT JUnitRunner that need special treatment.

Apparently, this special way of running GWT tests was added as a "temporary hack" , and in fact it doesn't look that great. But at least it works, and it's another proof that integrating GWT into a standard Java application is just not that easy.