Notes on a simple, but multimodule Java project

Russell Bateman
August 2021
last update:

The purpose of this write-up was to show the Maven file basis of a multi-module project. This project is conceived to be a web application in which there is a purely "domain" (or logic) component that might wish to be exported as a JAR for use in yet another project. (It's difficult to impossible to excerpt a subset of only some project classes as a JAR for use elsewhere—better to have it as its own, if dependently contributing project.)

As time goes, I will enhance this, but I'll be trying not to lose the essential simplicity of the project. I have already fleshed it out with Java code, a real web.xml, Maven dependencies, etc. and obtained a working project.

Parent pom.xml

The crucial components here are highlighted:

  1. Lines 7-10 specify the parent root; this must be evoked in dependent pom.xml files.
  2. Lines 13 and 14 specify what those other modules are (in which dependent pom.xml files exist).
  3. Lines 17-26 are property definitions that all pom.xml files will inherit—best to keep these in the parent.
  4. Lines 29-34 adn 48-52 are insignificant (related to JUnit testing).
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.windofkeltia</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <modules>
    <module>domain</module>
    <module>web</module>
  </modules>

  <properties>
    <junit.version>4.12</junit.version>
    <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
    <maven-war-plugin.version>3.2.0</maven-war-plugin.version>
    <maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>
    <maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

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

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven.compiler.plugin.version}</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${maven-surefire-plugin.version}</version>
      </plugin>
    </plugins>
  </build>
</project>

Domain pom.xml

  1. Lines 7-10 specify the JAR that the domain code will be built into.
  2. Lines 13-15 evoke (crucially) the parent project.
  3. Because of what Maven was created for and its Java-tight nature, you get domain-1.0.0.jar built for free—no need to configure a plug-in.
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.windofkeltia.domain</groupId>
  <artifactId>domain</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <parent>
    <groupId>com.windofkeltia</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <dependencies>
    <!-- JAR files that support domain work... -->
  </dependencies>
</project>

Web application pom.xml

  1. Lines 7-10 specify the JAR that the web-application code will be built into.
  2. Lines 13-15 evoke (crucially) the parent project.
  3. Lines 24-45 configure the Maven maven-war-plugin to build the WAR file.
  4. Line 29 you can also use "##" in the name and it has benefits, see this example.
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.windofkeltia.web</groupId>
  <artifactId>webapp</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <parent>
    <groupId>com.windofkeltia</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
  </parent>

  <dependencies>
    <!-- JAR files that support the web application/servlet... -->
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>${maven-war-plugin.version}</version>
        <configuration>
          <warName>${project.artifactId}-${project.version}</warName>
          <webResources>
            <resource>
              <directory>src/main/webapp</directory>
            </resource>
          </webResources>
          <archive>
            <manifest>
              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
            <manifestEntries>
              <Build-Time>${maven.build.timestamp}</Build-Time>
            </manifestEntries>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
       version="4.0">
  <display-name>parent</display-name>

  <servlet>
    <servlet-name>parent</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>

    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.windofkeltia.services</param-value>
      
    </init-param>

    
  </servlet>

  <servlet-mapping>
    <servlet-name>parent</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

The resulting filesystem...

You must also create the filesystem to look like this prior to setting up an IDE project, then establish the Maven files and add a minimal web.xml:

$ cd       ~/home/russ/dev/super
$ mkdir -p domain/src/main/java
$ mkdir -p domain/src/test/java
$ mkdir -p web/src/main/java
$ mkdir -p web/src/main/webapp/WEB-INF
$ touch    web/src/main/webapp/WEB-INF/web.xml
$ mkdir -p web/src/test/java
$ tree
.
├── domain
│   ├── pom.xml
│   └── src
│       ├── main
│       │   └── java
│       └── test
│           └── java
├── pom.xml
└── web
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   └── webapp
        │       └── WEB-INF
        │           └── web.xml
        └── test
            └── java

View of results

$ mvn clean
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent                                                             [pom]
[INFO] domain                                                             [jar]
[INFO] webapp                                                             [war]
[INFO]
[INFO] ----------------------< com.windofkeltia:parent >-----------------------
[INFO] Building parent 1.0.0                                              [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ parent ---
[INFO]
[INFO] ------------------< com.windofkeltia.domain:domain >--------------------
[INFO] Building domain 1.0.0                                              [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ domain ---
[INFO] Deleting /home/russ/dev/super/domain/target
[INFO]
[INFO] --------------------< com.windofkeltia.web:webapp >---------------------
[INFO] Building webapp 1.0.0                                              [3/3]
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ webapp ---
[INFO] Deleting /home/russ/dev/super/web/target
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for parent 1.0.0:
[INFO]
[INFO] parent ............................................. SUCCESS [  0.082 s]
[INFO] domain ............................................. SUCCESS [  0.003 s]
[INFO] webapp ............................................. SUCCESS [  0.002 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.150 s
[INFO] Finished at: 2021-08-10T15:22:26-06:00
[INFO] ------------------------------------------------------------------------

Building the project in an IDE...

If you open this project in IntelliJ IDEA doing the following...

  1. File &arr; New &arr; Project from Existing Sources.
  2. Navigate to parent project in filesystem and select.
  3. Click OK.
  4. Click Next to Create project from existing sources.
  5. Click Next to confirm chosen project name, location and format.
  6. Click Next to confirm content coming from filesystem point you chose.
  7. Click Finish to confirm that there were No frameworks detected.
  8. At bottom right of IDE, click Load Maven Projet.
  9. In dialog, Reload Maven Projects, click Recreate.

    It's important to note first that this is not the only way to set up a project from existing sources. It's probably not even the canonical way that JetBrains would preach to you, but it's an easy way for me coming from a command-line and 10-year Eclipse background.

    Second, realize that if you do not reach and complete this step as I have documented it, you're on your own to set up IDEA modules and marking the source and test code and resource filesystems appropriately, a tedious exercise to say the least. Recreate, whatever the strange semantics of that choice of words evokes, is your only salvation.

  10. Congratulations, your project is (probably) perfectly configured now, ready to begin working on.

...you should see something like this below. I called my project root super in the filesystem, but I could have given it a real name.