Modularizing GWT Applications with GWT-Maven
Last week, I spent some time modularizing the GWT application I'm working on. By modularizing, I mean splitting the code from one GWT module into a "core" and "webapp" module. The reason for doing this was so the "core" module could be used by another GWT application. Creating GWT Modules is fairly straightforward, but it wasn't as intuitive as expected when using the gwt-maven-plugin.
The hardest part of moving the code was figuring out how to run tests in the new "core" module. After getting it all working, it seems easy enough. Hopefully this post will make it easy for others. Here's the steps I'd recommend:
- Convert your GWT project into a multi-module project where you have a top-level pom.xml and two sub-modules (e.g. gwt-core and gwt-webapp).
- Do the normal single-to-multi-project Maven stuff like declaring the <parent> element in the modules and moving plugins/dependencies to the top-level pom.xml.
- Refactor your gwt-webapp project to push down all shared classes (and their tests) to gwt-core.
- In the gwt-core project, include *.xml and *.java in your JAR so GWT can extract/compile the source code when building gwt-webapp.
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.java</include> <include>**/*.xml</include> </includes> </resource> </resources>
- In
gwt-core/src/main/java
, create aCore.gwt.xml
that references the modules you'd like to use in all your applications. For example:<module> <inherits name="com.google.gwt.user.User"/> <inherits name="com.google.gwt.i18n.I18N"/> <inherits name="com.extjs.gxt.ui.GXT"/> <inherits name="pl.rmalinowski.gwt2swf.GWT2SWF"/> </module>
- Now the tricky part begins, mostly because of how the gwt-maven plugin currently works. In
src/test/java
, create aNoOpEntryPoint.gwt.xml
that inherits your Core module and defines an EntryPoint.<module> <inherits name="com.company.app.Core"/> <entry-point class="com.company.app.NoOpEntryPoint"/> </module>
- Create a
NoOpEntryPoint.java
class in the same directory asNoOpEntryPoint.gwt.xml
.public class NoOpEntryPoint implements EntryPoint { public void onModuleLoad() { // do nothing } }
- In any class that extends GWTTestCase (I usually create a parent class for all tests), reference the NoOpEntryPoint in the
getModuleName()
method.@Override public String getModuleName() { return "com.company.app.NoOpEntryPoint"; }
- Lastly, in the gwt-maven plugin's configuration (in gwt-core/pom.xml), reference the NoOpEntryPoint in <compileTargets>, a non-existent file in <runTarget> and only the "test" goal in the executions.
<plugin> <groupId>com.totsp.gwt</groupId> <artifactId>maven-googlewebtoolkit2-plugin</artifactId> <version>2.0-beta26</version> <configuration> <compileTargets> <value>com.company.app.NoOpEntryPoint</value> </compileTargets> <runTarget>com.company.app.NoOpEntryPoint/doesntexist.html</runTarget> <logLevel>INFO</logLevel> <style>OBF</style> <noServer>false</noServer> <extraJvmArgs>-Xmx512m</extraJvmArgs> <gwtVersion>${gwtVersion}</gwtVersion> <testFilter>*GwtTestSuite.java</testFilter> <testSkip>${skipTests}</testSkip> </configuration> <executions> <execution> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin>
The results of modularizing your application are beneficial (shared code) and detrimental (you have to mvn install
gwt-core whenever you make changes in shared classes). If you know of a way to configure the gwt-maven plugin to read sources from both gwt-core and gwt-webapp in hosted mode, I'd love to hear about it.