Build Tools: Maven & Gradle

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against GWT 2.13.1 (2026-06-19), gwt-maven-plugin 1.3.0 (2026-05-31), Maven 3.9.16, source level 17 and Jakarta Servlet 6.0 (the values the modular-webapp archetype 2026.6.22 generates), as of 2026-10-08. Every Maven command and every console output shown was run on that stack with JDK 21 (the same build also passes on JDK 26), except the Jetty command of the multi-module section, which was run on the scratch application earlier in the work on this section, and the CI workflow fragment. It is build and tooling material: nothing here is compiled to JavaScript, but the POM decides which of your sources are, which artifacts reach the server, and how the compiler is invoked. The compiler flags themselves belong to the Compiler & Compilation page, the module XML to Project Structure & Modules, and the run loop to Development Mode & Debugging; this page covers the build around them.

What the build has to do

A GWT application is three kinds of code that the Java tooling normally mixes together, and the build exists to keep them apart:

  • client code is compiled twice: once by javac (so the IDE and unit tests work) and once by the GWT compiler into JavaScript. The GWT compiler needs the Java sources of everything the client uses, not just class files;

  • shared code (DTOs, RPC interfaces) is compiled by javac for the server and, with its sources, by the GWT compiler for the client;

  • server code is ordinary Java for a servlet container and must never contain client-only classes, nor the other way round.

flowchart LR shared["bookshelf-shared
jar plus sources jar"] --> client["bookshelf-client
packaging gwt-app"] shared --> server["bookshelf-server
packaging war"] widgets["bookshelf-widgets
packaging gwt-lib"] --> client client -->|compiled JavaScript| warapp["gwt-app war
only compiler output"] warapp -->|overlay| server server --> deploy["bookshelf-server.war"]

The running scenario of this section, the Bookshelf, is exactly this shape: bookshelf-shared, bookshelf-client and bookshelf-server as generated by the net.ltgt.gwt.archetypes:modular-webapp archetype, plus a small bookshelf-widgets library module added on this page to show a gwt-lib. The sources a GWT compile reads come from the module XML <source> paths, so the build must make the sources of dependencies available on the compiler classpath. gwt-maven-plugin does this with two packagings (gwt-lib and gwt-app) and a set of goals, described next.

The org.gwtproject BOM and the GWT artifacts

Since 2.10.0 GWT publishes under the Maven groupId org.gwtproject (release notes 2.10.0); the former com.google.gwt coordinates stop at version 2.10.0. The bill of materials org.gwtproject:gwt:2.13.1 is a pom-type artifact that manages the versions of every GWT artifact, so that your POM states the GWT version exactly once:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.gwtproject</groupId>
      <artifactId>gwt</artifactId>
      <version>2.13.1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>6.0.0</version>
    </dependency>
  </dependencies>
</dependencyManagement>

Reading the 2.13.1 BOM (gwt-2.13.1.pom on Maven Central) gives the following picture. The version column is what the BOM pins; the last column says where the artifact belongs in the Bookshelf.

Table 1. Artifacts managed by the 2.13.1 BOM
Artifact What it contains Where it belongs

org.gwtproject:gwt-user

The client libraries (widgets, RPC client side, i18n, the com.google.gwt.* modules) with their sources. Its own dependencies are jsinterop-annotations 2.0.0, validation-api 1.0.0.GA (and its sources classifier), javax.servlet-api 3.1.0 and sac 1.3.

bookshelf-client and every gwt-lib module, default (compile) scope.

org.gwtproject:gwt-dev

The compiler, DevMode and the test runner (GWTTestCase) plus their dependencies (ASM, Jetty 9.4.58, HtmlUnit 2.55.0, JDT, Gson). Never deployed.

bookshelf-client, and provided scope in libraries so that it is not passed on to consumers.

org.gwtproject:gwt-codeserver

The Super Dev Mode code server.

Not declared in the Bookshelf POMs, and gwt:codeserver runs there.

org.gwtproject:gwt-servlet

The server-side runtime (RemoteServiceServlet and the serialisation classes) for the javax.servlet namespace.

Servers on Servlet 3.1 / 4.

org.gwtproject:gwt-servlet-jakarta

The same runtime for the jakarta.servlet namespace; the servlet base classes live in com.google.gwt.user.server.rpc.jakarta. Added in 2.11.0.

bookshelf-server (Servlet 6) and bookshelf-shared at provided scope.

org.gwtproject.web.bindery:requestfactory (imported pom)

RequestFactory: requestfactory-client, requestfactory-server, requestfactory-server-jakarta, requestfactory-apt. The groupId is not org.gwtproject.

Only if you use RequestFactory.

com.google.jsinterop:jsinterop-annotations

The JsInterop annotations, pinned to 2.0.0 by the BOM (2.1.0 exists on Maven Central).

Arrives transitively with gwt-user.

com.google.gwt:gwt-user, gwt-dev, gwt-codeserver, gwt-servlet at 2.10.0

Kept in the BOM, according to a comment in the POM, so that build tools can relocate the old groupId correctly.

Do not depend on them.

The BOM also pins third-party libraries that the tools use (gson, htmlunit, ASM, Jetty 9.4.58, icu4j, JDT), which is why they appear in a dependency:tree of any module that declares gwt-dev. A real excerpt, from the bookshelf-widgets module of this page (library with gwt-user at compile scope and gwt-dev at provided scope):

[INFO] com.example.bookshelf:bookshelf-widgets:gwt-lib:1.0-SNAPSHOT
[INFO] +- org.gwtproject:gwt-user:jar:2.13.1:compile
[INFO] |  +- com.google.jsinterop:jsinterop-annotations:jar:2.0.0:compile
[INFO] |  +- javax.validation:validation-api:jar:1.0.0.GA:compile
[INFO] |  +- javax.validation:validation-api:jar:sources:1.0.0.GA:compile
[INFO] |  +- javax.servlet:javax.servlet-api:jar:3.1.0:compile
[INFO] |  \- org.w3c.css:sac:jar:1.3:compile
[INFO] \- org.gwtproject:gwt-dev:jar:2.13.1:provided
[INFO]    +- com.google.code.findbugs:jsr305:jar:1.3.9:provided
[INFO]    +- com.google.code.gson:gson:jar:2.11.0:provided

Two consequences matter in practice:

  • gwt-user brings javax.servlet-api 3.1.0 even when your server is Jakarta. Keep the real server API (jakarta.servlet:jakarta.servlet-api, provided) in the server module, use gwt-servlet-jakarta there, and never put gwt-user on the server classpath of a deployed WAR: it duplicates gwt-servlet.

  • gwt-dev is large (a complete Jetty and HtmlUnit). A gwt-app module can declare it at the default scope because the gwt-app WAR contains only the compiler output, but a library should declare it provided, otherwise every consumer inherits it.

gwt-maven-plugin

The Maven integration used by the archetypes, by the GWT project’s own samples (moved to Maven in 2.13.0) and by this section is net.ltgt.gwt.maven:gwt-maven-plugin, written by Thomas Broyer. Its design goals are stated on its site: it is independent of the GWT version (it passes explicit argument lists to GWT instead of modelling every flag), it understands multi-module projects, and it adds two packagings and two artifact types. It is tested against the latest two stable GWT versions and the latest snapshot.

<plugin>
  <groupId>net.ltgt.gwt.maven</groupId>
  <artifactId>gwt-maven-plugin</artifactId>
  <version>1.3.0</version>
  <extensions>true</extensions>
  <configuration>
    <moduleName>com.example.bookshelf.App</moduleName>
    <moduleShortName>bookshelf</moduleShortName>
    <sourceLevel>17</sourceLevel>
    <failOnError>true</failOnError>
  </configuration>
</plugin>

<extensions>true</extensions> is what makes the gwt-app and gwt-lib packagings (and artifact types) known to Maven; without it Maven answers Unknown packaging: gwt-app. The usage page still shows BOM 2.13.0 while the plugin 1.3.0 is the latest release; use the BOM version you actually run. failOnError makes the compile fail when sources have errors instead of producing a partial result, and the generated POMs of both the archetype and webAppCreator set it. sourceLevel is explained under the compile goal.

The default project layout, which every goal assumes unless you reconfigure it:

Table 2. Default layout of a plugin-managed module
Path Use

src/main/module.gwt.xml

Hand-written module template, processed by gwt:generate-module. The rename-to attribute and some <inherits> are generated.

src/main/java/, src/main/resources/

Standard Java sources and resources; GWT also reads them as sources, so *.ui.xml, *.gss and images live here.

src/main/super/

GWT super-sources, added by gwt:add-super-sources.

src/test/java/, src/test/resources/

Standard test sources; GWTTestCase classes and the test modules (*.gwt.xml) live here.

src/test/super/

Test super-sources, added by gwt:add-test-super-sources.

Packagings and lifecycles

The plugin contributes two lifecycles, each triggered by a new <packaging>. They look like the jar and war lifecycles with different default bindings, and the bindings below are exactly those of the plugin’s Lifecycles page.

flowchart TB subgraph lib["packaging gwt-lib"] direction LR l1["initialize
enforce-encoding, add-super-sources,
add-test-super-sources"] --> l2["generate-resources
generate-module,
generate-module-metadata"] l2 --> l3["compile and test-compile
javac"] --> l4["test
gwt:test"] --> l5["package
package-lib"] end subgraph app["packaging gwt-app"] direction LR a1["initialize
same three goals"] --> a2["generate-resources
generate-module"] a2 --> a3["compile and test-compile
javac"] --> a4["test
gwt:test"] --> a5["prepare-package
gwt:compile"] --> a6["package
package-app"] end
Table 3. Default goal bindings of the two lifecycles
Phase gwt-lib gwt-app

initialize

gwt:enforce-encoding, gwt:add-super-sources, gwt:add-test-super-sources

the same three goals

generate-resources

gwt:generate-module, gwt:generate-module-metadata

gwt:generate-module

process-resources, compile, process-test-resources, test-compile

resources:resources, compiler:compile, resources:testResources, compiler:testCompile

the same (no testResources binding is listed for gwt-app)

test

gwt:test

gwt:test

prepare-package

none

gwt:compile

package

gwt:package-lib

gwt:package-app

install, deploy

install:install, deploy:deploy

install:install, deploy:deploy

You can see the bindings working in a real run. This is mvn package -DskipTests for the bookshelf-widgets (gwt-lib) module; the lines that are not GWT goals are omitted:

[INFO] ------------------------------[ gwt-lib ]-------------------------------
[INFO] --- gwt:1.3.0:enforce-encoding (default-enforce-encoding) @ bookshelf-widgets ---
[INFO] --- gwt:1.3.0:add-super-sources (default-add-super-sources) @ bookshelf-widgets ---
[INFO] --- gwt:1.3.0:add-test-super-sources (default-add-test-super-sources) @ bookshelf-widgets ---
[INFO] --- gwt:1.3.0:generate-module (default-generate-module) @ bookshelf-widgets ---
[INFO] --- gwt:1.3.0:generate-module-metadata (default-generate-module-metadata) @ bookshelf-widgets ---
[INFO] --- compiler:3.15.0:compile (default-compile) @ bookshelf-widgets ---
[INFO] --- gwt:1.3.0:test (default-test) @ bookshelf-widgets ---
[INFO] --- gwt:1.3.0:package-lib (default-package-lib) @ bookshelf-widgets ---
[INFO] Building jar: .../bookshelf-widgets/target/bookshelf-widgets-1.0-SNAPSHOT.jar

The gwt-app module of the same build shows gwt:compile running at prepare-package and the output being packaged:

[INFO] ------------------------------[ gwt-app ]-------------------------------
[INFO] --- gwt:1.3.0:enforce-encoding (default-enforce-encoding) @ bookshelf-client ---
[INFO] --- gwt:1.3.0:add-super-sources (default-add-super-sources) @ bookshelf-client ---
[INFO] --- gwt:1.3.0:add-test-super-sources (default-add-test-super-sources) @ bookshelf-client ---
[INFO] --- gwt:1.3.0:generate-module (default-generate-module) @ bookshelf-client ---
[INFO] --- gwt:1.3.0:test (default-test) @ bookshelf-client ---
[INFO] --- gwt:1.3.0:compile (default-compile) @ bookshelf-client ---
[INFO] Compiling module com.example.bookshelf.App
[INFO]    Compiling 6 permutations
[INFO]    Compilation succeeded -- 15,479s
[INFO] Linking into .../bookshelf-client/target/bookshelf-client-1.0-SNAPSHOT/bookshelf
[INFO] --- gwt:1.3.0:package-app (default-package-app) @ bookshelf-client ---
[INFO] Building war: .../bookshelf-client/target/bookshelf-client-1.0-SNAPSHOT.war

The two artifact types

The same two names are also dependency types. They describe what the artifact contains, and therefore how a consumer may use it.

Table 4. The gwt-lib and gwt-app artifacts
Type Content How to depend on it

gwt-lib

A JAR with both compiled classes and their sources: the generated module descriptor (<slashified moduleName>.gwt.xml), META-INF/gwt/mainModule (optional), **/*.java, **/*.class and other resources such as *.gss, *.png and *.ui.xml.

<type>jar</type> or <type>gwt-lib</type>.

gwt-app

A WAR holding only the compiler output: <moduleShortName>/<moduleShortName>.nocache.js and the other files under <moduleShortName>/. No WEB-INF/web.xml, no classes, no libraries.

<type>war</type> or <type>gwt-app</type>. With war the maven-war-plugin overlays the content into the resulting WAR automatically.

The entries of bookshelf-client-1.0-SNAPSHOT.war in this run confirm the second row: a manifest, the permutation script bookshelf/342AE3F7D9EDD9BB635FB4F8BFDC783C.cache.js, bookshelf/bookshelf.devmode.js, the serialization policy bookshelf/85B004BBB10BADC9964AD6DE9572205E.gwt.rpc and the theme images. The server module reaches the same files through the overlay: bookshelf-server-1.0-SNAPSHOT.war contains bookshelf/bookshelf.nocache.js (checked with unzip -l).

The goals

The plugin has twelve goals (the plugin-info page and mvn gwt:help agree; help is the standard Maven plugin-plugin goal). mvn net.ltgt.gwt.maven:gwt-maven-plugin:1.3.0:help prints This plugin has 12 goals, and with -Ddetail=true -Dgoal=<name> the parameters of one goal.

Table 5. Every goal of gwt-maven-plugin 1.3.0
Goal Default phase Purpose

gwt:enforce-encoding

initialize

Enforces that the source encoding is UTF-8. No parameters.

gwt:add-super-sources

initialize

Adds the super-source directory to the project resources.

gwt:add-test-super-sources

initialize

The same for the test resources.

gwt:generate-module

generate-resources

Generates the GWT module descriptor from the template and the dependencies.

gwt:generate-module-metadata

generate-resources

Writes META-INF/gwt/mainModule, read by generate-module in downstream projects.

gwt:compile

prepare-package

Invokes the GWT compiler (production mode).

gwt:test

test

Runs GWTTestCase tests (a Surefire-based runner).

gwt:package-app

package

Packages the compiled application into a WAR-like archive.

gwt:package-lib

package

Packages the library into a JAR.

gwt:codeserver

none (aggregator)

Runs the Super Dev Mode code server.

gwt:devmode

none (aggregator)

Runs DevMode, for standalone applications.

gwt:help

none

Displays help for the plugin; does not need a project.

gwt:codeserver and gwt:devmode are aggregator goals that require dependency resolution at compile+runtime scope and first execute the process-classes phase of the modules they run; the other goals resolve compile (compile), runtime (generate-module) or test (test) dependencies.

The compile goal

gwt:compile runs the GWT compiler in a forked JVM. Its parameters (every one of them is documented on the goal page; the descriptor inside the 1.3.0 JAR gives the defaults used here) are:

Table 6. Parameters of gwt:compile
Parameter Property or default Meaning

moduleName (required)

none

The module to compile, for example com.example.bookshelf.App. Also used by gwt:codeserver and gwt:devmode to find the modules to run.

moduleShortName

none

The short name used for the output directory and the .nocache.js file; becomes the rename-to of the generated module.

sourceLevel

maven.compiler.source

Java source level passed to the compiler (-sourceLevel). When it is empty the compiler’s own default (1.8) applies.

style

gwt.style

Script output style: OBFUSCATED, PRETTY or DETAILED.

optimize

gwt.optimize

Optimisation level, 0 (none) to 9 (maximum).

draftCompile

gwt.draftCompile, default false

Faster, less optimised compilations.

localWorkers

gwt.localWorkers

Number of local workers compiling permutations; a number followed by C is multiplied by the CPU core count (floating point only with C).

failOnError

gwt.failOnError

Succeed only if no input files have errors.

logLevel

gwt.logLevel

Level of logging detail.

compilerArgs

list

Additional arguments passed to the compiler, in the order given (ordering matters for some GWT flags).

jvmArgs, jvm, jdkToolchain

none

Arguments for the forked JVM (for example -Xmx), the Java executable to use, or the JDK toolchain requirements. Without jvm the configured toolchain is used, falling back to the JVM that runs Maven.

systemProperties

map

System properties passed to the compiler.

webappDirectory

${project.build.directory}/${project.build.finalName}

The target web application directory.

workDir, deploy, extra

target/gwt/work, target/gwt/deploy, none

The compiler work directory, the directory for deployable but not servable files, and the directory for extra files not intended for deployment.

skipCompilation

gwt.skipCompilation, default false

Skips the compiler. Documented use: quickly package an incomplete or stale application that is used as a dependency (an overlay) in a WAR.

forceCompilation

gwt.forceCompilation, default false

Compiles even if the output looks up to date.

staleMillis

lastModGranularityMs, default 0

Granularity in milliseconds of the last-modified comparison used for the up-to-date check.

A few behaviours deserve a real transcript. Compilation is skipped when the output is up to date: running the same build a second time took 0.9 seconds in total (the first one took 17 seconds, six permutations):

$ mvn -B package -DskipTests -pl bookshelf-client -am
[INFO] --- gwt:1.3.0:compile (default-compile) @ bookshelf-client ---
[INFO] Compilation output seems uptodate. GWT compilation skipped.
[INFO] --- gwt:1.3.0:package-app (default-package-app) @ bookshelf-client ---
[INFO] BUILD SUCCESS
[INFO] Total time:  0.946 s

With -Dgwt.skipCompilation=true the goal only logs GWT compilation is skipped and the application WAR is built without compiler output. With the properties of the table you steer a single run from the command line, here a draft compile with readable output and two workers, forced because the output was already up to date:

mvn -B package -DskipTests -pl bookshelf-client -am \
    -Dgwt.draftCompile=true -Dgwt.style=PRETTY -Dgwt.localWorkers=2 -Dgwt.forceCompilation=true

That run finished in 11.3 seconds for the whole reactor, against 18.7 seconds for the first full build (a draft compile also turns the -Xnamespace default to PACKAGE, hence the new warnings Namespace option is not compatible with CodeSplitter, turning it off, which can be ignored).

Which sourceLevel is used is the most frequent surprise. The property it reads is maven.compiler.source, and maven-compiler-plugin projects that only set maven.compiler.release (the modern way) leave it empty. Both generated POMs therefore set <sourceLevel> explicitly: the archetype uses 17 together with <release>17</release> on the compiler plugin, and the webAppCreator Maven template uses maven.compiler.release 11 with a comment explaining that the compiler of 2.12.0 and later needs level 11 or above. The Compiler -help text and the Developer Guide are right that the command-line compiler’s own default is 1.8 (the default argument registered in ToolBase), so Java 12 to 17 syntax needs an explicit -sourceLevel 17, and auto resolves to the level of the running JVM. The plugin’s <sourceLevel> is therefore not the compiler’s default but whatever maven.compiler.source holds; setting the level explicitly avoids depending on either (details on the Compiler & Compilation page).

Further compiler flags go in compilerArgs. The Bookshelf enables the generation of JsInterop exports this way:

<configuration>
  <moduleName>com.example.bookshelf.App</moduleName>
  <moduleShortName>bookshelf</moduleShortName>
  <compilerArgs>
    <arg>-generateJsInteropExports</arg>
  </compilerArgs>
</configuration>

The code server and DevMode goals

gwt:codeserver is the Maven way to run Super Dev Mode. It is an aggregator goal, so its configuration belongs in the root POM, and it gathers the gwt-app modules of the reactor by itself. The plugin documentation lists its differences from gwt:compile:

  • the applications and all reactor modules depended on with <type>gwt-lib</type> or <classifier>sources</classifier> have their source roots used as they are, so edits are seen immediately (a resource directory with filtering or relocation behaves differently);

  • the dependency scope for the classpath is runtime by default rather than compile, configurable with classpathScope;

  • since GWT 2.7 you should set launcherDir, a folder your web server or servlet container reads static resources from, so that the *.nocache.js written by the code server is loadable and the browser’s "recompile on reload" works;

  • because the goal triggers process-classes in all modules, restrict the reactor with -pl and add -am, or set projects (-Dgwt.projects=) in a profile;

  • because the code server takes different arguments from the compiler, arguments needed by both are defined twice, in compilerArgs and in codeserverArgs.

Table 7. Parameters of gwt:codeserver
Parameter Property or default Meaning

launcherDir

launcherDir

Where the launcher files (*.nocache.js) are written.

codeserverWorkDir

target/gwt/codeserver

Work directory (must be writable).

classpathScope

runtime

Dependency scope (compile, runtime, compile+runtime, runtime+system or test).

modules

gwt.modules

Comma-delimited modules to run; defaults to the module names discovered from gwt-app projects.

projects

gwt.projects

Comma-delimited reactor projects to run; defaults to all gwt-app projects. It can also run non-gwt-app projects (for example a war) together with modules.

codeserverArgs

list

Additional arguments for the code server.

sourceLevel, style, failOnError, logLevel

maven.compiler.source, gwt.style, gwt.failOnError, gwt.logLevel

As for gwt:compile.

jvm, jvmArgs, jdkToolchain, systemProperties

none

The forked JVM, as for gwt:compile.

The archetype’s root POM holds the configuration that makes the aggregator goal work, with <inherited>false</inherited> so that it applies to the root only. This is the Bookshelf version, with a codeserverArgs entry added that moves the code server to port 9951 (the default is 9876) so that it cannot clash with another instance:

<plugin>
  <groupId>net.ltgt.gwt.maven</groupId>
  <artifactId>gwt-maven-plugin</artifactId>
  <inherited>false</inherited>
  <configuration>
    <launcherDir>${basedir}/bookshelf-server/target/gwt/launcherDir</launcherDir>
    <codeserverArgs>
      <arg>-port</arg>
      <arg>9951</arg>
    </codeserverArgs>
  </configuration>
</plugin>

Running it with the reactor restricted to the client and its dependencies (the log is shortened):

$ mvn gwt:codeserver -pl bookshelf-client -am
[INFO] --- gwt:1.3.0:codeserver (default-cli) @ bookshelf ---
[INFO] Turning off precompile in incremental mode.
[INFO] Super Dev Mode starting up
[INFO]    workDir: .../target/gwt/codeserver
[INFO]    Loading Java files in com.example.bookshelf.App.
[INFO]    Module setup completed in 5285 ms
[INFO] The code server is ready at http://127.0.0.1:9951/

GET /recompile-requester/bookshelf answered 200 on that port. The goal executes at the root project (@ bookshelf), not in the client module, which is the visible sign of an aggregator. The archetype README and some guides still write the wildcard form -pl *-client, and it does not work: Maven 3.9.16 stops with Could not find the selected project in the reactor: *-client; wildcards are a Maven 4 feature. Use -pl bookshelf-client or -pl :bookshelf-client.

gwt:devmode is the same idea for the classic DevMode and for standalone applications, that is, client-only code (offline applications, or applications talking to a remote server through CORS) for which a code server serving a host page from elsewhere is less comfortable. Its extra parameters are devmodeArgs, devmodeWorkDir (default target/gwt/devmode/work), warDir (default target/gwt/devmode/war, mandatory but with a default; roughly the counterpart of launcherDir) and startupUrls, a list of URLs to open automatically that should point to a host page in the module’s public path. The plugin documentation discourages using it with a web application (it needs war:exploded first) because the plugin documentation considers the webapp class loader inside DevMode unreliable. The 2.13.0 release notes note that DevMode now serves only static files by default, ; the Bookshelf uses gwt:codeserver.

<plugin>
  <groupId>net.ltgt.gwt.maven</groupId>
  <artifactId>gwt-maven-plugin</artifactId>
  <configuration>
    <warDir>${project.build.directory}/gwt/devmode/war</warDir>
    <startupUrls>
      <startupUrl>index.html</startupUrl>
    </startupUrls>
  </configuration>
</plugin>

The test goal

gwt:test runs tests that extend GWTTestCase (see Testing) with the setup they need (the GWT test runner, HtmlUnit by default). It is built on Surefire, so most parameters (includes, excludes, test, skipTests, forkCount, argLine, groups, reportsDirectory and so on) behave as in Surefire, and the plugin warns that part of its documentation is inherited from Surefire and may be wrong or irrelevant. The GWT-specific ones are:

Table 8. GWT-specific parameters of gwt:test
Parameter Property or default Meaning

testArgs

list

Additional arguments for the JUnitShell (the test runner).

compilerArgs, useCompilerArgsForTests

list, default false

Compiler arguments, and whether they are prepended to testArgs (reuse is possible only when the compiler arguments are compatible with the test shell).

port, codeServerPort

gwt.port, gwt.codeServerPort

TCP port of the embedded web server and of the code server (both pick a free port by default).

outDir, deploy, extra, workDir

target/gwt-tests/www, target/gwt-tests/deploy, none, target/gwt/work

Output directories.

draftCompile, style, optimize, localWorkers, logLevel, sourceLevel

as for gwt:compile

Compilation of the tests' module.

enableAssertions

enableAssertions, default true

JVM assertions are on by default for the tests, which activates GWT’s internal consistency checks (see the permutation pitfall below).

includes

list

Test file patterns.

junitArtifactName

default junit:junit

The JUnit artifact the runner looks for.

The pitfalls found while running the Bookshelf tests are important enough to show verbatim. First, in this setup the goal ran no tests at all without an explicit <includes>, and printed nothing about it (the lifecycle output for the module was just the goal header followed by the next goal). The archetype sets an explicit <includes><include>**/*Test.java</include></includes> for that reason:

<configuration>
  <moduleName>com.example.bookshelf.App</moduleName>
  <includes>
    <include>**/*Test.java</include>
  </includes>
</configuration>

Second, -Dtest= is applied to every module of the reactor, and a module without a matching test fails:

$ mvn verify -pl bookshelf-client -am -Dtest=BookshelfGwtTest
[ERROR] Failed to execute goal net.ltgt.gwt.maven:gwt-maven-plugin:1.3.0:test (default-test) on project bookshelf-widgets:
No tests matching pattern "BookshelfGwtTest" were executed! (Set -DGWT tests.failIfNoSpecifiedTests=false to ignore this error.)

The hint in the message has a typo; the property from the descriptor is surefire.failIfNoSpecifiedTests:

$ mvn verify -pl bookshelf-client -am -Dtest=BookshelfGwtTest -Dsurefire.failIfNoSpecifiedTests=false
[INFO] --- gwt:1.3.0:test (default-test) @ bookshelf-client ---
[INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider
[INFO]  T E S T S
[INFO] Running com.example.bookshelf.client.BookshelfGwtTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 12.05 s -- in com.example.bookshelf.client.BookshelfGwtTest

Third, use mvn verify (or install), not mvn test, for a client module whose shared dependency is consumed through its sources classifier JAR: that JAR is only produced at package, and mvn test -pl bookshelf-client -am fails with No source code is available for type com.example.bookshelf.shared.Book; did you forget to inherit a required module?. A test with several permutations also fails under -ea in com.google.gwt.dev.Permutation.assertSameAnswers unless the test module pins a single locale and user.agent (the dedicated test modules of the Bookshelf do that); the Testing page covers the test side.

Module generation goals

gwt:generate-module is what lets a Maven project avoid maintaining <inherits> lines by hand. It reads the template (src/main/module.gwt.xml by default) and writes the real descriptor to the class output, in the package of moduleName. The description in the descriptor is precise:

  • META-INF/gwt/mainModule files from the project’s direct dependencies (not transitive) produce <inherits> directives, inserted at the very beginning of the generated module, before any <inherits> of the template;

  • if moduleShortName is given, it overwrites any rename-to of the template;

  • unless the template contains a <source> or <super-source> element, the lines <source path="client"/>, <source path="shared"/> and <super-source path="super"/> are appended at the end.

Table 9. Parameters of gwt:generate-module and gwt:generate-module-metadata
Parameter Default Meaning

moduleName

none

The module to generate (both goals).

moduleShortName

none

Used for rename-to and for the .nocache.js name.

moduleTemplate

src/main/module.gwt.xml

Module definition to merge with. A missing file is the same as an empty one.

outputDirectory

target/classes (module) or target/classes/META-INF/gwt (metadata)

Where the descriptor, respectively mainModule, is written.

generateInheritsFromDependencies

true

Generate <inherits> from the mainModule files of dependencies.

skipModule

false

Disable generation in favour of a hand-written descriptor (see the migration section).

skipModuleMetadata

false

Disable generation of mainModule.

The effect is visible in the Bookshelf. The bookshelf-widgets library declares com.example.bookshelf.widgets.Widgets as its module name. Its template (the whole file) is:

<module>
  <inherits name="com.google.gwt.user.User"/>
  <source path="client"/>
</module>

The build wrote the descriptor to com/example/bookshelf/widgets/Widgets.gwt.xml (identical to the template, since there is no short name and no dependency with a mainModule), and the one-line metadata file META-INF/gwt/mainModule containing the module name. After bookshelf-client declared a dependency on that library, the generated target/classes/com/example/bookshelf/App.gwt.xml of the client starts with:

<module rename-to="bookshelf">
  <inherits name="com.example.bookshelf.widgets.Widgets"/>
  <inherits name="com.google.gwt.user.User"/>
  <inherits name="com.google.gwt.user.theme.clean.Clean"/>

The new <inherits> line was never written by hand, and the rename-to="bookshelf" came from moduleShortName. That is also the reason for the order rule: the generated inherits come before the template’s, so a template that depends on the order of inheritance (for overrides, for example) must say so with its own explicit line.

Super-source goals

gwt:add-super-sources adds src/main/super (parameter superSourceDirectory) to the resources, and gwt:add-test-super-sources adds src/test/super (testSuperSourceDirectory). GWT requires super-sources to live in a subdirectory of the module; with relocateSuperSource (respectively relocateTestSuperSource) set to true the plugin relocates the directory’s content into a super subfolder of the module named by moduleName, and the <super-source path="super"/> line from the generation rules above then makes the sources visible. Both parameters default to false. The two goals are bound to initialize; the Bookshelf has no super-sources, which the build reports as skip non existing resourceDirectory .../src/main/super.

<configuration>
  <moduleName>com.example.bookshelf.widgets.Widgets</moduleName>
  <superSourceDirectory>src/main/super</superSourceDirectory>
  <relocateSuperSource>true</relocateSuperSource>
</configuration>

Packaging goals

gwt:package-app assembles the gwt-app WAR from the compiler output, and gwt:package-lib the gwt-lib JAR.

Table 10. Parameters of gwt:package-app and gwt:package-lib
Parameter Default Meaning

warName (app)

${project.build.finalName}

Name of the generated WAR.

outputDirectory

${project.build.directory}

Where the archive is written.

classesDirectory (lib)

${project.build.outputDirectory}

Directory whose classes and resources go into the JAR.

finalName (lib)

${project.build.finalName}

Name of the JAR.

forceCreation

war.forceCreation (app), maven.jar.forceCreation (lib), false

Rebuild even if the contents appear unchanged. By default an existing archive is kept when inputs did not change; this does not work if another plugin (such as the shade plugin) post-processes the archive.

archive

none

A Maven Archiver configuration.

The listing of the library JAR built in this run shows what gwt-lib means. It has the sources next to the classes, the generated module and the metadata:

META-INF/MANIFEST.MF
META-INF/gwt/mainModule
com/example/bookshelf/widgets/Widgets.gwt.xml
com/example/bookshelf/widgets/client/StockBadge.java
com/example/bookshelf/widgets/client/StockBadge.class
META-INF/maven/com.example.bookshelf/bookshelf-widgets/pom.xml
META-INF/maven/com.example.bookshelf/bookshelf-widgets/pom.properties

Multi-module builds

The three-module split is not decoration: it is how the plugin keeps the three code kinds apart. The Bookshelf reactor, in the order Maven resolves it from the dependencies, is bookshelf-widgets (gwt-lib), bookshelf-shared (jar), bookshelf-client (gwt-app) and bookshelf-server (war). The relationships to set up are:

<!-- bookshelf-client/pom.xml -->
<packaging>gwt-app</packaging>
<dependencies>
  <dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>bookshelf-shared</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>bookshelf-shared</artifactId>
    <version>${project.version}</version>
    <classifier>sources</classifier>
  </dependency>
  <dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>bookshelf-widgets</artifactId>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupId>org.gwtproject</groupId>
    <artifactId>gwt-user</artifactId>
  </dependency>
  <dependency>
    <groupId>org.gwtproject</groupId>
    <artifactId>gwt-dev</artifactId>
  </dependency>
</dependencies>

The shared module needs a sources classifier JAR (the client module lists it as a second dependency) because the compiler reads Java source; the maven-source-plugin execution jar-no-fork bound to package in the root pluginManagement produces it. A gwt-lib needs none of that because its JAR already contains sources, which is why a pure client library should be a gwt-lib and a library shared with the server should stay a plain JAR with sources.

The server module depends on the client’s WAR (<type>war</type>, runtime scope) and the maven-war-plugin overlays it:

<!-- bookshelf-server/pom.xml, profile env-prod (active by default) -->
<dependency>
  <groupId>${project.groupId}</groupId>
  <artifactId>bookshelf-client</artifactId>
  <version>${project.version}</version>
  <type>war</type>
  <scope>runtime</scope>
</dependency>

The archetype wraps that dependency in a profile pair. env-prod is active by default and adds the client WAR; env-dev is activated by -Denv=dev (or -Penv-dev, as the archetype README says) and instead points the Jetty plugin at the launcherDir written by the code server, so the server can be started without compiling the application. The comment in the generated POM explains the odd shape: Maven forces an explicit env-prod profile to be able to exclude the dependency in env-dev. The effect, measured on the Bookshelf, was that the server WAR built with -Denv=dev contained no nocache.js (0 matches), while the default build contained one:

# Development: the code server runs in one terminal, the server in another
mvn gwt:codeserver -pl bookshelf-client -am
mvn -Denv=dev -pl bookshelf-server -am org.eclipse.jetty.ee10:jetty-ee10-maven-plugin:12.1.10:run

# Production: compiles the client and overlays it
mvn clean package

The reactor options to remember are -pl (select projects), -am (also make the projects they depend on), -amd (also make the projects depending on them) and -rf (resume from). The first command above works because -pl bookshelf-client -am selects the client, the shared module and the library, and because the aggregator goal runs once at the root. In a shell, use plain project names, as noted before.

Skipping GWT work in CI profiles

Compiling GWT is the slowest step of a CI job (17 seconds for the small Bookshelf, minutes for a real application), and most jobs do not need it: a server-only test run, a lint job, or a documentation job. Because the plugin parameters are bound to properties, a profile can switch work off without editing any plugin configuration.

<profiles>
  <profile>
    <id>no-gwt</id>
    <properties>
      <gwt.skipCompilation>true</gwt.skipCompilation>
      <skipTests>true</skipTests>
    </properties>
  </profile>
  <profile>
    <id>ci-fast</id>
    <build>
      <pluginManagement>
        <plugins>
          <plugin>
            <groupId>net.ltgt.gwt.maven</groupId>
            <artifactId>gwt-maven-plugin</artifactId>
            <configuration>
              <draftCompile>true</draftCompile>
              <style>PRETTY</style>
              <localWorkers>2</localWorkers>
            </configuration>
          </plugin>
        </plugins>
      </pluginManagement>
    </build>
  </profile>
</profiles>

Run with -Pno-gwt the whole reactor built in 3.4 seconds, all four modules SUCCESS, with the log lines GWT compilation is skipped for the client and Tests are skipped. for each module. That is the right profile for a job that only needs to know that the Java sources compile. The ci-fast profile keeps the compile but cuts its cost; it is suitable for pull-request builds that run the browser tests, while the release build runs without it so that the shipped JavaScript is optimised and obfuscated. Guidelines that follow from the descriptor:

  • Prefer properties for toggles (gwt.skipCompilation, gwt.draftCompile, gwt.style, gwt.localWorkers) and profiles for presets. The plugin has few properties by design because it passes arguments to GWT explicitly; the migration page of the plugin suggests presets in profiles and the attribute combine.children="append" to extend an existing compilerArgs list from a profile.

  • gwt.localWorkers accepts a number or a number with C: -Dgwt.localWorkers=0.5C uses half the cores, useful on shared CI runners.

  • Do not set skipCompilation for the build that produces the artifact you deploy: a gwt-app WAR built that way is valid but empty of JavaScript, and the server WAR overlays it without complaint.

  • The plugin decides that a compile can be skipped by checking that the output *.nocache.js exists and that the POM, sources and dependencies have not changed (the documented behaviour of forceCompilation). For a release build, pass -Dgwt.forceCompilation=true so that a stale target/ directory restored from a CI cache can never ship.

  • GWT tooling needs Java 11 or newer to run (the 2.13.0 notes say that 2.13 is probably the last release whose compiler and dev tools run on Java 11), so pin the CI JDK to a version your GWT release supports and set sourceLevel explicitly.

# GitHub Actions job (fragment): server tests on every push, full GWT build only on the main branch
- uses: actions/setup-java@v4
  with:
    distribution: temurin
    java-version: 21
    cache: maven
- run: mvn -B -Pno-gwt verify
- run: mvn -B clean verify
  if: github.ref == 'refs/heads/main'

The Maven commands of the fragment were run as shown; the workflow file itself was not executed.

Packaging reusable GWT libraries as JARs

A library that other GWT projects will inherit must ship its sources, because the consumer’s compiler translates them. The module descriptor and any public resources must be on the classpath next to them. Doing this by hand means placing src/main/java in the resources (or building a second JAR with the maven-source-plugin and a sources classifier, as the shared module does) and writing the .gwt.xml correctly. The plugin automates the first variant, which is why a gwt-lib is the recommended packaging for client-only libraries:

  1. set <packaging>gwt-lib</packaging>;

  2. set <moduleName> to the fully qualified module the library exposes;

  3. keep a src/main/module.gwt.xml template with the <inherits> of the other GWT libraries and the <source> paths if they are not the defaults (the plugin appends client, shared and super source paths when the template has none);

  4. declare gwt-user normally and gwt-dev as provided;

  5. install or deploy it: the consumer depends on it with the usual <dependency> (the type defaults to jar, which is accepted).

The consumer needs no <inherits> line when it uses gwt:generate-module, because META-INF/gwt/mainModule from the direct dependency generates it, and it needs one if the module template is hand-written and generation is skipped. A library that is not a gwt-lib but publishes a sources classifier is consumed as in the Bookshelf’s bookshelf-shared: both the binary and the sources dependency, and <inherits> of the module (or the mainModule mechanism when the producer uses gwt:generate-module-metadata).

What goes wrong is nearly always one of two things, both visible as compiler errors: the sources are not on the classpath (No source code is available for type com.example.bookshelf.shared.Book; did you forget to inherit a required module?), or the module is not inherited. The next pitfall table collects them. The module XML itself (the <source>, <public> and <inherits> elements and the standard module hierarchy) is explained on the Project Structure & Modules page; the official reference is the Developer Guide chapter on modules.

The legacy Codehaus plugin and migrating from it

Before Thomas Broyer’s plugin, the Maven plugin for GWT was org.codehaus.mojo:gwt-maven-plugin (goal prefix gwt, the same prefix, which makes it easy to confuse the two). Its last release is 2.10.0 (the Maven Central metadata was last updated on 2022-06-25), and its build properties show it was built against GWT 2.10.0 (gwt.version). It is tied to one GWT version, which is the root of most migration work. Its goals, read from the plugin.xml of the 2.10.0 JAR:

Table 11. Goals of org.codehaus.mojo:gwt-maven-plugin 2.10.0
Goal Default phase Purpose (descriptor)

compile

prepare-package

Invokes the GWT compiler.

run

none

Runs the project in GWT classic or Super Dev Mode.

run-codeserver

none

Runs GWT modules with Super Dev Mode.

debug

none

Runs with a debugger port hook, optionally suspended.

test

integration-test

Mimics Surefire to run `GWTTestCase`s.

resources, source-jar

process-resources, package

Copy GWT sources and the module descriptor as resources, respectively into the project JAR.

mergewebxml

process-resources

Merges GWT servlet elements into the deployment descriptor.

generateAsync

generate-sources

Generates the Async interface of an RPC service.

i18n, css

generate-sources

Generates i18n interfaces from properties files, and CSS interfaces from CSS files.

clean, compile-report, eclipse, eclipseTest, help

none

Housekeeping, the compile report, Eclipse launch configurations.

Goals such as generateAsync, i18n, css, mergewebxml and eclipse have no equivalent in the new plugin; the plugin’s migration page says that most goals of the old plugin have none. The differences it lists are:

Table 12. Migrating from the Codehaus plugin
Codehaus (Mojo) plugin net.ltgt.gwt.maven plugin

Tied to one GWT version through its own dependencies

Independent of the GWT version. You must add the dependencies gwt-dev (and gwt-user) to your project, preferably through the org.gwtproject:gwt BOM.

The module is wherever you keep it (src/main/java/.../App.gwt.xml)

The module is managed in src/main/module.gwt.xml by default. Either move your module file there or set skipModule to true to disable gwt:generate-module.

gwt:compile, gwt:run-codeserver, to some extent gwt:run

The only goals with equivalents: gwt:compile, gwt:codeserver and (to some extent) gwt:devmode. All others have none.

One project holds the whole application

gwt:codeserver and gwt:devmode are designed for multi-module projects; the client and server are separate modules.

Dozens of per-flag parameters (for example optimizationLevel, draftCompile, compileReport, extraJvmArgs, runTarget)

Few properties. Arguments are passed explicitly through list properties (compilerArgs, codeserverArgs, devmodeArgs, testArgs), which makes the plugin usable with many GWT versions and lets you control the argument order (important for GWT in some cases). Prepare command-line variations as profiles and use combine.children="append".

A practical migration order that keeps the project building at every step:

  1. Replace the plugin coordinates and set <extensions>true</extensions>; keep the module descriptor where it is by setting skipModule to true (the lowest-risk route) and move to src/main/module.gwt.xml later.

  2. Import the org.gwtproject:gwt BOM and declare gwt-user and gwt-dev; remove the version properties of the old plugin that selected the GWT version.

  3. Move the old <configuration> entries into moduleName, moduleShortName, sourceLevel, style, draftCompile, jvmArgs and compilerArgs.

  4. Replace gwt:run-codeserver by gwt:codeserver at the root of a multi-module build, with launcherDir.

  5. Replace gwt:source-jar and gwt:resources by the gwt-lib packaging for client libraries.

  6. Split the server out into its own module and overlay the gwt-app WAR on it.

  7. Delete the use of gwt:i18n, gwt:css, gwt:generateAsync, gwt:mergewebxml and write what they generated.

A concrete failure reproduces the skipModule pitfall. When the library module had skipModule set to true but no descriptor under target/classes, gwt:codeserver at the root failed with:

[INFO]    Loading inherited module 'com.example.bookshelf.App'
[INFO]       Loading inherited module 'com.example.bookshelf.widgets.Widgets'
[INFO]          [ERROR] Unable to find 'com/example/bookshelf/widgets/Widgets.gwt.xml' on your classpath; could be a typo, or maybe you forgot to include a classpath entry for source?
[ERROR] Failed to execute goal net.ltgt.gwt.maven:gwt-maven-plugin:1.3.0:codeserver (default-cli) on project bookshelf: Process exited with an error: 1 (Exit value: 1)

Take care with the groupId during the move: GWT’s coordinates changed from com.google.gwt to org.gwtproject at 2.10.0, so an old POM that declares com.google.gwt:gwt-user:2.10.0 still resolves, but nothing newer is published there.

Gradle

GWT itself has no official Gradle plugin and no Gradle page in its documentation: the Developer Guide, the Getting Started page and the release notes mention Ant and Maven only. Gradle support is entirely community maintained. The state of the plugins on 2026-10-08, read from the Gradle Plugin Portal and the project repositories:

Table 13. Gradle plugins for GWT (status checked 2026-10-08)
Plugin Version and activity Notes from its README

org.docstr.gwt (repository jiakuan/gwt-gradle-plugin, "v2")

2.2.9, published 2026-01-31; repository last pushed 2026-06-16

A rewrite of the original plugin. Does not require you to declare gwt-user, gwt-dev and gwt-codeserver. Supports GWT 2.12 and later; minimum Gradle 8.1. Tasks gwtCompile (run by gradle build), gwtDevMode and gwtSuperDev. The gwt { modules = [...] } block takes the modules; the plugin’s default GWT version is documented in its README as 2.12.1.

de.esoco.gwt and de.esoco.gwt-lib (repository esoco/gwt-gradle-plugin)

1.3.1, published 2026-05-12; repository last pushed 2026-05-12

A fork of the unmaintained Putnami plugin adapted to GWT 2.13.0. Requires Java 17 and Gradle 8 or later; 1.3.0 added compatibility with Gradle 9.

steffenschaefer/gwt-gradle-plugin

archived (last push 2017)

Do not use.

The minimal setup of the first plugin, as its README documents it:

plugins {
  id "org.docstr.gwt" version "2.2.9"
}

gwt {
  modules = ['com.example.bookshelf.App']
}

I did not run either Gradle plugin, so nothing about their task behaviour or compatibility with the Bookshelf is claimed beyond what the READMEs say. If you choose Gradle, the equivalent of the multi-module layout above is a client project that applies the GWT plugin, a shared project that exposes its sources to the client, and a server project; check how the plugin you pick makes the sources of the shared project available to the compiler, since that is the part Maven solves with the sources classifier and gwt-lib. Maven remains the build documented and exercised throughout this section, because gwt-maven-plugin, the archetypes and the GWT project’s own samples are Maven.

The command-line creator tools

Before archetypes, the GWT SDK shipped command-line generators. They are history, and the official pages that still describe them have not kept up.

webAppCreator is a script in the SDK that runs com.google.gwt.user.tools.WebAppCreator from gwt-user. Its documented parameters are -[no]overwriteFiles, -[no]ignoreExistingFiles, -templates (default sample,ant,eclipse,readme), -out, -junit, the deprecated -[no]maven and -[no]ant, and the module name. The 2.11.0 release notes list the tool as deprecated. The class itself prints this on every run (real output on 2.13.1):

java -cp gwt-user-2.13.1.jar:gwt-dev-2.13.1.jar com.google.gwt.user.tools.WebAppCreator \
     -out foo -templates maven,sample,readme com.example.foo.Foo
WebAppCreator is deprecated - please refer to https://www.gwtproject.org/gettingstarted.html for other ways to create a new application with GWT, including with Maven archetypes. These encourage better separation of classpaths, and do not need IDE-specific setup.
Generating from templates: [maven, readme, _sample-test, sample]
Created directory foo
Created file foo/pom.xml
Created file foo/src/main/java/com/example/foo/Foo.gwt.xml
Created file foo/src/main/java/com/example/foo/client/Foo.java
Created file foo/src/main/java/com/example/foo/server/GreetingServiceImpl.java
Created file foo/src/main/webapp/WEB-INF/web.xml

What it generates is a single-module war project, the opposite of the layout recommended above. Its pom.xml (2.13.1) still pins gwt-maven-plugin 1.2.0, runs the compile and test goals, sets maven.compiler.release to 11 and sourceLevel to 11, writes the classes into WEB-INF/classes and disables Surefire in favour of gwt:test. It passes -compileReport and -XcompilerMetrics in compilerArgs (see Compile Reports & Optimization); the latter is accepted by the compiler but is not listed in the compiler’s -help output. The official Getting Started page (gettingstarted.html) still tells you to unpack gwt-2.10.0.zip, requires Java 1.8 and Apache Ant, and uses the default Ant template with ant devmode and ant build, so it is stale; the same is true of the Compile & Debug chapter, which launches DevMode through ant devmode. The deprecation, not those pages, is current. Use the archetype instead:

mvn archetype:generate \
    -DarchetypeGroupId=net.ltgt.gwt.archetypes -DarchetypeArtifactId=modular-webapp -DarchetypeVersion=2026.6.22 \
    -DgroupId=com.example.bookshelf -DartifactId=bookshelf -Dmodule-short-name=bookshelf

i18nCreator (com.google.gwt.i18n.tools.I18NCreator) generates a properties file and a script that regenerates the Java interface (a Constants, ConstantsWithLookup or Messages subinterface) when the properties change. It is not deprecated in the source and still runs. Real output for a Messages interface:

$ java -cp gwt-user-2.13.1.jar:gwt-dev-2.13.1.jar com.google.gwt.i18n.tools.I18NCreator \
       -createMessages com.example.foo.client.FooMessages
Created file src/com/example/foo/client/FooMessages.properties
Created file FooMessages-i18n

The generated script calls com.google.gwt.i18n.tools.I18NSync with the classpath of the machine where it was generated, which makes it non-portable. The Internationalization page recommends writing the Messages interface first and the properties files from it, which is the direction Maven builds prefer.

Tools the first book taught

GWT in Action by Hanson and Tacy (Manning, 2007) was written for GWT 1.x and shows a toolchain that no longer exists in GWT 2.13.1: the projectCreator and applicationCreator scripts (an Eclipse-oriented pair run in that order), junitCreator, i18nCreator and the shell and compile scripts generated beside the application, which started the hosted browser and the old com.google.gwt.dev.GWTCompiler. In the source of 2.13.1:

Table 14. The book’s tools and what became of them
Tool in the book Today

GWTShell and "hosted mode" (the shell script that launched the hosted browser)

GWTShell was removed in 2.5.1; the terms changed to "development mode" and "production mode" in 2.0, with the entry point com.google.gwt.dev.DevMode. Super Dev Mode replaced it for daily work.

com.google.gwt.dev.GWTCompiler

com.google.gwt.dev.Compiler (no GWTCompiler class exists in the 2.13.1 sources).

projectCreator, applicationCreator

Replaced by webAppCreator; the leftover no-op main() classes ApplicationCreator and ProjectCreator in gwt-user were removed in 2.12.0.

junitCreator

Removed in 2.0.0 RC2, with its function moved into the -junit option of webAppCreator; today the modular-webapp archetype contains the test setup.

i18nCreator

Still present.

Ant build.xml and Eclipse .launch files

Maven (gwt-app/gwt-lib packagings) or a community Gradle plugin; IDE import of the POM.

Use the book for the concepts of chapters 2 and 9 (the development cycle, the module concept, packaging a module as a JAR with sources), and this page for how those concepts are built today.

Pitfalls

Table 15. Build problems and fixes
Symptom or message Cause Fix

Unknown packaging: gwt-app (and Unknown packaging: gwt-lib), reported as The build could not read 2 projects

The plugin is declared without <extensions>true</extensions>.

Add the extensions flag to the plugin declaration (in pluginManagement or build/plugins).

No source code is available for type com.example.bookshelf.shared.Book; did you forget to inherit a required module?

The module is not inherited, or the dependency has no sources JAR on the compiler classpath (for instance, running mvn test before the sources classifier JAR is packaged).

Declare both the binary and the sources classifier dependency, add <inherits>, and run mvn verify or install.

gwt:test prints nothing and reports no tests

includes is not configured for the goal in this setup.

Add <includes> with **/*Test.java or pass -Dtest=....

No tests matching pattern "..." were executed!

-Dtest is applied to every module of the reactor.

Add -Dsurefire.failIfNoSpecifiedTests=false.

Could not find the selected project in the reactor: *-client

A wildcard in -pl (Maven 4 only).

Use -pl bookshelf-client.

Unable to find '.../Widgets.gwt.xml' on your classpath

A module descriptor was never generated (for instance skipModule is true without a hand-written file) or the library is not on the classpath.

Remove skipModule, or put the descriptor where the module name says.

GWT compile is skipped and the application is empty

gwt.skipCompilation is set (a CI profile that leaked into the release build).

Build the artifact with -Dgwt.forceCompilation=true and no skip profile.

The compile uses a different Java level from javac

maven.compiler.source is empty because only maven.compiler.release is set.

Set <sourceLevel> in the plugin configuration.

Ignoring incompatible plugin version 4.0.0-beta-... (maven-install-plugin, maven-deploy-plugin)

Maven 3.9 skipping newer plugin betas that need Maven 4.

Harmless noise; pin versions in pluginManagement to silence it.

GWT-RPC call fails with InaccessibleObjectException for java.lang.Throwable.detailMessage on Java 17 or later

Server-side exception serialisation needs reflection into java.lang; upstream issue gwtproject/gwt#9793.

Start the servlet container JVM with --add-opens java.base/java.lang=ALL-UNNAMED (the Maven Jetty plugin: MAVEN_OPTS).

Recent changes and where the books are outdated

  • 2.10.0: the groupId becomes org.gwtproject; the BOM org.gwtproject:gwt carries the managed versions.

  • 2.11.0: Jakarta artifacts (gwt-servlet-jakarta, requestfactory-server-jakarta); webAppCreator deprecated.

  • 2.12.0: minimum Java 11 for the tools; Java 12 to 17 language levels (hence sourceLevel 17 in the archetype); the ApplicationCreator and ProjectCreator main classes removed.

  • 2.13.0: the samples of the GWT repository moved to Maven; DevMode serves only static files by default; the notes say that 2.13 is probably the last release whose compiler and dev tools run on Java 11.

  • Plugin 1.3.0 (2026-05-31) and the archetype 2026.6.22 target 2.13.x; the archetype pins jetty-ee10-maven-plugin 12.1.10 for the server.

  • Gradle: still no official plugin; two maintained community plugins, one archived.

  • The 2007 book (first edition) describes Ant, projectCreator/applicationCreator, the hosted browser and the GWTCompiler class: none of that exists. The second edition of the book (2013) has a Manning page that resolves but was not consulted for this page. The Google-hosted Getting Started and Compile & Debug pages also still describe the Ant flow of GWT 2.10.