I admit that I was a bit slow to see the value of Maven. Perhaps I had too many years steeped in Unix and make, but ant felt comfortable and Maven did not, at least initially. Suffice it to say that I’m a convert, and now find myself disappointed when I can’t manage a project with Maven. (Android projects are still challenging, for example.)

I’ve been working on a Google Web Toolkit (GWT) project for a while using Maven. There have been some minor challenges, so in this article I thought I’d summarize some of the nits I uncovered during the process.

Dependencies

My core GWT library dependencies look like this:

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>
		<gwt.version>2.5.1</gwt.version>
		...
	</properties>
	<dependencies>
		...
		<dependency>
			<groupId>com.google.gwt</groupId>
			<artifactId>gwt-servlet</artifactId>
			<version>${gwt.version}</version>
		</dependency>

		<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-dev</artifactId>
			<version>${gwt.version}</version>
			<scope>test</scope>
		</dependency>
		...
	<dependencies>

Note that gwt-user doesn’t have a compile scope the way that one usually associates with dependencies, since its contents are actually translated into JavaScript. Similarly, gwt-dev is only needed for development purposes, and so is scoped test so that it will not be bundled into the final product, but to be available during the test phase.

Build plugin

The build process for a GWT project differs considerably from that of a conventional WAR project. The gwt-maven-plugin handles this. The build definition I use looks like this:

		<plugins>
			<!-- GWT Maven Plugin -->
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>gwt-maven-plugin</artifactId>
				<version>${gwt.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>compile</goal>
							<goal>test</goal>
							<goal>generateAsync</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<runTarget>index.html</runTarget>
					<hostedWebapp>${webappDirectory}</hostedWebapp>
					<extraJvmArgs>-XX:MaxPermSize=512m -Xmx1024m -Xss10m</extraJvmArgs>
					<gwtSdkFirstInClasspath>true</gwtSdkFirstInClasspath>
				</configuration>
			</plugin>
			...
		<plugins>

In my project, we are not currently using GWT’s I18N facility. (We will probably do so eventually, but right now we’re single-language.) If you’re using them, you’ll want to add an additional

<goal>i18n</goal>

to the execution section above. The extraJvmArgs bumps up the available memory for the build process – GWT builds are a bit on the memory-intensive side.

The gwtSdkFirstInClasspath is a lifesaver. GWT has its own process for compiling Java into JavaScript. Unfortunately, their own implementation conflicts with almost any package that uses the standard jdtcore packages to do runtime compilation. In our case, for example, we use JasperReports, which has a jdtcore dependency in order to enable it to compile reports at runtime. The upshot is that you can get java.lang.NoSuchMethodExceptions at build time because GWT is looking for things that are in its own version of the classes but which aren’t in the mainline one. Adding the gwtSdkFirstInClasspath parameter set to true tells the plugin to put the GWT build jars first in the classpath, which eliminates the compile-time issue. It is possible, of course, that this could also be handled by careful ordering of the dependencies in pom.xml (since Maven uses this to establish classpath order), but this can be a real nuisance to try to work out.

Building the WAR

When you’re getting ready to deploy your application, you typically want a fully-built WAR file. During development, however, the best way to set things up is to have Maven build an “exploded” WAR that can be used by the version of Jetty that is bundled with GWT. Fortunately, there’s a way to do both. Configuring the maven-war-plugin as shown:

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<executions>
					<execution>
						<id>explode-during-compile</id>
						<phase>compile</phase>
						<goals>
							<goal>exploded</goal>
						</goals>
					</execution>
					<execution>
						<id>war-during-package</id>
						<phase>package</phase>
						<goals>
							<goal>war</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<webappDirectory>${webappDirectory}</webappDirectory>
				</configuration>
			</plugin>

will cause the exploded version to be constructed as part of the compilation process, but the “all in one” version to be constructed as part of the package phase.

Eclipse

I use Eclipse for my day-to-day development. Because of the customizations of the GWT build process, Eclipse needs to be told how to go about integrating with Maven to make sure everything builds properly.

First, I have the Google Plugin for Eclipse and M2E plugins installed.

Second, my pom.xml contains the following:

		<pluginManagement>
			<plugins>
				<!-- Make Eclipse happy -->
				<plugin>
					<groupId>org.eclipse.m2e</groupId>
					<artifactId>lifecycle-mapping</artifactId>
					<version>1.0.0</version>
					<configuration>
						<lifecycleMappingMetadata>
							<pluginExecutions>
								<pluginExecution>
									<pluginExecutionFilter>
										<groupId>org.codehaus.mojo</groupId>
										<artifactId>gwt-maven-plugin</artifactId>
										<versionRange>[2.4.0,)</versionRange>
										<goals>
											<goal>resources</goal>
											<goal>compile</goal>
											<goal>i18n</goal>
											<goal>generateAsync</goal>
										</goals>
									</pluginExecutionFilter>
									<action>
										<execute />
									</action>
								</pluginExecution>
								<pluginExecution>
									<pluginExecutionFilter>
										<groupId>org.apache.maven.plugins</groupId>
										<artifactId>maven-war-plugin</artifactId>
										<versionRange>[2.1.1,)</versionRange>
										<goals>
											<goal>exploded</goal>
										</goals>
									</pluginExecutionFilter>
									<action>
										<execute />
									</action>
								</pluginExecution>
							</pluginExecutions>
						</lifecycleMappingMetadata>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>

This makes sure that the appropriate goals on the gwt-maven-plugin and maven-war-plugin are called when the project is being built by Eclipse instead of via the mvn command line. The M2E plugin will monitor your pom.xml file, and will warn you if you make changes to it that require Eclipse to update its own build settings. The latter is generally a matter of using the “Update Project…” feature of the M2E plugin.

Compile Report

The GWT compile report is useful if you’re going to analyze code splitting. This can be includes as follows:

	<reporting>
		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>gwt-maven-plugin</artifactId>
				<version>{gwt.version}</version>
				<reportSets>
					<reportSet>
						<reports>
							<report>compile-report</report>
						</reports>
					</reportSet>
				</reportSets>
			</plugin>
		</plugins>
	</reporting>

Issues

GWT is still not, in my opinion, a “first class citizen” when it comes to Maven. In particular, as of GWT 2.5.1, GWT is still bundling “foreign” packages into its jar files instead of declaring transitive dependencies. This, unfortunately, causes problems if you’re trying to use those same dependencies in your project. One of the more painful sets are the servlet classes – gwt-servlet includes classes for Servlet 2.5, which pretty well precludes using both gwt-servlet and Servlet 3.0 in a standard Maven build.

There is an outstanding GWT Issue #4484 to address this – hopefully it will gain some momentum.

I also have a small test class that is useful to hook the copy of Jetty that GWT embeds into JUnit tests. But that, dear reader, is for another post.