gwt-cx: A Maven Multi-module Enterprise Project

In this post, we're going to create a Maven multi-module enterprise project by refactoring the CRMdipity (Se-r-en-dipity) project.

In this post, we'll:

  1. Generate the project's directory structure
  2. Create the project's parent POM
  3. Create the gwtcx-core POM and the gwtcx-samples POM
  4. Create the client module
  5. Create the shared module
  6. Create the server module
  7. Create the sample web application (Serendipity)
  8. Build the multi-module project
  9. Run the sample web application

Note: You can download the project from the gwt-cx download page and ask questions and make comments or suggestions in the gwt-cx discussion group.

1. Generate the project's directory structure

At the heart of Maven is a simple concept — convention over configuration. This means that toolkits, frameworks and applications should assume reasonable defaults (without requiring any unnecessary configuration) and that systems should "just work".

A key illustration of this concept is Maven's default behaviours for projects. For example, source code is assumed to be in $(basedir)/src/main/java and resources are assumed to be in $(basedir)/src/main/resources. Tests are assumed to be in $(basedir)/src/test and a project is assumed to produce a JAR (Java ARchive) file. Maven also assumes that you want to compile byte code to $(basedir)/target/classes and then create a distributable JAR file in $(basedir)/target.

You can generate the directory structure for a project by hand or you can use the Maven Archetype plugin as discussed in this post. However, because we're refactoring an existing code base I found this easiest to do by using a combination of the Eclipse IDE and the command line.

 

You can read more about Maven's adoption of convention over configuration in the Maven Getting Started Guide.

2. Create the project's parent POM

Large Maven projects are often divided into sub-projects. A multi-module project is defined by a parent POM that references one or more submodules.

The gwtcx/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"

  ...

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.gwtcx</groupId>
  <artifactId>gwtcx</artifactId>
  <version>0.7.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  <name>The GWT Customer Experience framework</name>
  <inceptionYear>2011</inceptionYear>
  <description>gwt-cx is an open-source Customer Experience (CX) framework.</description>
  <url>http://code.google.com/p/gwt-cx/</url>

  <modules>
    <module>gwtcx-core</module>
    <module>gwtcx-samples</module>
  </modules>

  <properties>
    <target.jdk>1.6</target.jdk>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <gwt-maven-plugin.version>2.2.0</gwt-maven-plugin.version>
    <maven-compiler-plugin.version>2.3.2</maven-compiler-plugin.version>

    <gwt.version>2.2.0</gwt.version>
    <gwtp.version>0.5.1</gwtp.version>
    <junit.version>4.8.1</junit.version>

    ...

  </properties>

  <build>
    <pluginManagement>
      <plugins>

        <!-- Maven Compiler Plugin -->
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
          <configuration>
            <source>${target.jdk}</source>
            <target>${target.jdk}</target>
            <encoding>${project.build.sourceEncoding}</encoding>
          </configuration>
        </plugin>

        ...

      </plugins>
    </pluginManagement>

  <dependencyManagement>
    <dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

</project>

Take a look at the parent POM and you will notice that it defines a set of Maven coordinates: the groupid is com.gwtcx, the artifactid is gwtcx, and the version is 0.7.0-SNAPSHOT. The parent project doesn't create a JAR or a WAR instead it is simply a POM that refers to other Maven projects.

The next section in the POM lists the project's submodules. Modules are defined in the modules element and each module element corresponds to a subdirectory of the gwtcx directory. Maven knows to look in these directories for pom.xml files and to add these to the list of Maven projects included in a build.

You can also define settings that will be inherited by all submodules in the parent POM. Both the build configuration and dependencies are inherited by all submodules.

3. Create the gwtcx-core POM and the gwtcx-samples POM

As this is a large Maven project it's also a good idea to separate the framework (gwtcx/gwtcx-core POM) from the samples (gwtcx/gwtcx-samples POM).

The gwtcx/gwtcx-core/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  
  ...

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.gwtcx</groupId>
    <artifactId>gwtcx</artifactId>
    <version>0.7.0-SNAPSHOT</version>
  </parent>

  <artifactId>gwtcx-core</artifactId>
  <packaging>pom</packaging>
  <name>GWTCX Core</name>

  <modules>
    <module>gwtcx-core-client</module>
    <module>gwtcx-core-shared</module>
    <module>gwtcx-core-server</module>
  </modules>

  ...

</project>

Take a look at the gwtcx/gwtcx-core/pom.xml and you will notice that it references a parent POM using a set of Maven coordinates. The next section in the POM lists the project's submodules: the client module, the shared module, and the server module.

The gwtcx/gwtcx-samples/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" 

  ...
  
  <modelVersion>4.0.0</modelVersion>
  
  <parent>
    <groupId>com.gwtcx</groupId>
    <artifactId>gwtcx</artifactId>
    <version>0.7.0-SNAPSHOT</version>
  </parent>
  
  <artifactId>gwtcx-samples</artifactId>
  <packaging>pom</packaging>
  <name>GWTCX Samples</name>
  
  <modules>
    <module>gwtcx-sample-serendipity</module>
  </modules>
  
  ...

</project>

In the gwtcx/gwtcx-samples/pom.xml you can see that this POM also references a parent POM using a set of Maven coordinates. The next section in the POM lists the project's submodules: the serendipity module.

4. Create the client module

The gwtcx-core-client module is the first module referenced in the gwtcx-core POM. This project contains the framework's client-side source files and subpackages.

The gwtcx/gwtcx-core/gwtcx-core-client/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" 

  ...
  
  <modelVersion>4.0.0</modelVersion>
  
  <parent>
    <groupId>com.gwtcx</groupId>
    <artifactId>gwtcx-core</artifactId>
    <version>0.7.0-SNAPSHOT</version>
  </parent>
  
  <artifactId>gwtcx-core-client</artifactId>
  <packaging>jar</packaging>
  <name>GWTCX Core - Client</name>

  <build>
    <resources>
    
      <!-- bundle sources with the jar, so they are visible to GWT's compiler -->
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.java</include>
        </includes>
      </resource>
      
      <!-- bundle the module descriptor with the jar, so it is visible to GWT's compiler -->      
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.gwt.xml</include>
        </includes>
       </resource>
       
     </resources>
  </build>

  <dependencies>
  
    <!-- GWT Customer Experience dependencies -->
    <dependency>
      <groupId>${project.groupId}</groupId>
      <artifactId>gwtcx-core-shared</artifactId>
    </dependency>    
  
    <!-- Google Web Toolkit dependencies -->
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-dev</artifactId>
    </dependency>        

    ... 

  </dependencies>  
</project>

In the gwtcx/gwtcx-core/gwtcx-core-client/pom.xml you can see that this POM references a parent POM using a set of Maven coordinates: the groupid is com.gwtcx, the artifactid is gwtcx-core, and the version is 0.7.0-SNAPSHOT. The next section in the POM defines this project's artifactid: gwtcx-core-client and packaging: jar. The project inherits its parent POM's groupid and version.

The resources element includes the resource declarations to bundle sources and module descriptors with the JAR (gwtcx-core-client-0.7.0-SNAPSHOT.jar).

5. Create the shared module

The gwtcx-core-shared module is the second module referenced in the gwtcx-core POM. This project contains the source files and subpackages that are used by both the client and the server components of the framework. For example, Data Transfer Objects (DTOs) and gwtp-dispatch's Actions and Results.

The gwtcx/gwtcx-core/gwtcx-core-shared/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"

  ...

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.gwtcx</groupId>
    <artifactId>gwtcx-core</artifactId>
    <version>0.7.0-SNAPSHOT</version>
  </parent>

  <artifactId>gwtcx-core-shared</artifactId>
  <packaging>jar</packaging>
  <name>GWTCX Core - Shared</name>

  <properties>
    <generated-sources>target/generated-sources/apt</generated-sources>
  </properties>

  <build>
    <resources>

      <!-- bundle sources with the jar, so they are visible to GWT's compiler -->
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.java</include>
        </includes>
      </resource>

      <!-- bundle generated sources with the jar, so they are visible to GWT's compiler -->
      <resource>
        <directory>${generated-sources}</directory>
        <includes>
          <include>**/*.java</include>
        </includes>
      </resource>

      <!-- bundle module descriptor with the jar, so it is visible to GWT's compiler -->
    <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.gwt.xml</include>
        </includes>
      </resource>

    </resources>

    ...

  </build>

  ...

</project>

As in the client POM the shared POM references a parent POM using a set of Maven coordinates. The next section in the POM defines this project's artifactid: gwtcx-core-shared and packaging: jar. And, as in the client POM the shared project inherits its parent POM's groupid and version.

The resources element includes the resource declarations to bundle sources, generated sources and module descriptors with the JAR (gwtcx-core-shared-0.7.0-SNAPSHOT.jar).

6. Create the server module

The gwtcx-core-server module is the third module referenced in the gwtcx-core POM. This project contains the framework's server-side code and subpackages.

The gwtcx/gwtcx-core/gwtcx-core-server/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"

  ...

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.gwtcx</groupId>
    <artifactId>gwtcx-core</artifactId>
    <version>0.7.0-SNAPSHOT</version>
  </parent>

  <artifactId>gwtcx-core-server</artifactId>
  <packaging>jar</packaging>
  <name>GWTCX Core - Server</name>

  <dependencies>

    <!-- GWT Customer Experience dependencies -->
    <dependency>
      <groupId>${project.groupId}</groupId>
      <artifactId>gwtcx-core-shared</artifactId>
    </dependency>

    <!-- Google Web Toolkit dependencies -->
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
    </dependency>

    ...

  </dependencies>
</project>

In the gwtcx/gwtcx-core/gwtcx-core-server/pom.xml we can see that this POM references a parent POM using a set of Maven coordinates: the groupid is com.gwtcx, the artifactid is gwtcx-core, and the version is 0.7.0-SNAPSHOT. The next section in the POM defines this project's artifactid: gwtcx-core-server and packaging: jar. The project inherits its parent POM's groupid and version.

The dependencies element includes a dependency declaration for gwt-cx's shared module.

7. Create the sample web application (Serendipity)

The sample web application (Serendipity) is the first module referenced in the gwtcx-samples POM. This project depends on gwt-cx's client, shared and server modules.

The gwtcx/gwtcx-samples/gwtcx-sample-serendipity/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" 

  ...

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.gwtcx</groupId>
    <artifactId>gwtcx-samples</artifactId>
    <version>0.7.0-SNAPSHOT</version>
  </parent>

  <artifactId>gwtcx-sample-serendipity</artifactId>
  <packaging>war</packaging>
  <name>GWTCX Sample - Serendipity</name>

  <build>
    <plugins>

      ...

    </plugins>
  </build>

  <dependencies>

    <!-- GWT Customer Experience dependencies -->
    <dependency>
      <groupId>com.gwtcx</groupId>
      <artifactId>gwtcx-core-client</artifactId>
    </dependency>
    <dependency>
      <groupId>com.gwtcx</groupId>
      <artifactId>gwtcx-core-server</artifactId>
    </dependency>

    <!-- Google Web Toolkit dependencies -->
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
    </dependency>

    ...

  </dependencies>
</project>

Take a look at the gwtcx/gwtcx-samples/gwtcx-sample-serendipity/pom.xml and you will notice that it is follows the same conventions as the framework's other POMs. It references a parent POM using a set of Maven coordinates and then defines the project's artifactid: gwtcx-sample-serendipity and packaging: war. And, as in the client, shared and server modules the sample web application project inherits its parent POM's groupid and version.

The dependencies element includes dependency declarations for gwt-cx's client and server modules (the shared module is a transitive dependency so there's no need to explicitly declare it).

8. Build the multi-module project

Now we're ready to compile and package the sample web application into a WAR file. To do this, you will need to compile and install the projects in the right order. You can do this by simply running mvn clean install from the gwtcx project's base directory:

~/gwtcx$ mvn clean install

[INFO] Scanning for projects...
[INFO] Reactor build order: 
[INFO]   The GWT Customer Experience framework
[INFO]   GWTCX Core
[INFO]   GWTCX Core - Shared
[INFO]   GWTCX Core - Client
[INFO]   GWTCX Core - Server
[INFO]   GWTCX Samples
[INFO]   GWTCX Sample - Serendipity
[INFO] ------------------------------------------------------------------------
[INFO] Building The GWT Customer Experience framework
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\tools\IDEs\helios\workspace\gwtcx\pom.xml to ...
[INFO] ------------------------------------------------------------------------
[INFO] Building GWTCX Core
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
[INFO] [gwt:test {execution: default}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\tools\IDEs\helios\workspace\gwtcx\gwtcx-core\pom.xml ...
[INFO] ------------------------------------------------------------------------
[INFO] Building GWTCX Core - Shared
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] ...
[INFO] ------------------------------------------------------------------------
[INFO] Building GWTCX Core - Client
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] ...
[INFO] ------------------------------------------------------------------------
[INFO] Building GWTCX Core - Server
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] ...
[INFO] ------------------------------------------------------------------------
[INFO] Building GWTCX Samples
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing C:\tools\IDEs\helios\workspace\gwtcx\gwtcx-samples\pom.xml to ...
[INFO] ------------------------------------------------------------------------
[INFO] Building GWTCX Sample - Serendipity
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] ...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] The GWT Customer Experience framework ................. SUCCESS [1.706s]
[INFO] GWTCX Core ............................................ SUCCESS [2.648s]
[INFO] GWTCX Core - Shared ................................... SUCCESS [3.076s]
[INFO] GWTCX Core - Client ................................... SUCCESS [33.394s]
[INFO] GWTCX Core - Server ................................... SUCCESS [1.823s]
[INFO] GWTCX Samples ......................................... SUCCESS [0.017s]
[INFO] GWTCX Sample - Serendipity ............................ SUCCESS [1:48.212s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 minutes 31 seconds
[INFO] Finished at: Sat Sep 10 07:41:17 EST 2011
[INFO] Final Memory: 59M/171M
[INFO] ------------------------------------------------------------------------

When Maven is run against a project with submodules, Maven first loads the parent POM and looks for any submodules. Maven then puts all the project's POMs into the Maven Reactor which analyses the dependencies between modules. The Reactor takes care of the ordering of components to ensure that they are built in the proper order.

9. Run the sample web application

Once the multimodule project has been built you can change directories into the gwtcx-sample-serendipity project and execute the "run" goal of the "gwt" plugin.

~/gwtcx-sample-serendipity$ mvn gwt:run

If you have the gwt-maven-plugin installed then right-click on the sample web application's pom.xml in the Project Explorer and choose "Run As -> Maven Build..." and execute the run goal of the gwt plugin:

 

Note: You can download the project from the gwt-cx download page and ask questions and make comments or suggestions in the gwt-cx discussion group.

Comments

Great article!

I'm a big fan of your articles, and you have a great collection here.

I just wanted to ask, why did you choose to separate the client, shared and server modules instead of creating packages inside a single maven module? The only reason I can think of is not to bundle server package's source in the generated jar.

Thanks!

Hi, Thanks for the

Hi,

Thanks for the feedback.

Large Maven projects are often divided into sub-projects (modules).

Take a look at this example:

-> http://www.sonatype.com/books/mvnex-book/reference/multimodule-web-sprin...

Cheers
Rob