- What is POM
- Super POM
- Minimal POM
- Project Inheritance
- Project Aggregation
- Comparison between Inheritance and Aggregation
- Project Interpolation and Variables
What is POM
The Project Object Model, or POM, serves as the cornerstone of Maven. It’s an XML file containing project information and detailed configurations for Maven to build the project. It encapsulates default values for most project parameters. For instance, the build directory defaults to target
, source code resides in src/main/java
, test source code in src/test/java
, and so forth. When tasks or goals are executed, Maven looks for the POM in the current directory. It reads the POM, extracts the required configuration, and proceeds with the goals.
Configurations that can be specified within the POM include project dependencies, executable plugins or goals, build profiles, and more. Additionally, project version, description, developers, mailing lists, among other details, can be specified.
Super POM
The Super POM represents Maven’s default POM. Unless explicitly overridden, all POMs extend the Super POM, implying that any POM you create for your project inherits configurations specified in the Super POM.
You can explore the Super POM for Maven 3.6.3 in the Maven Core Reference Documentation.
Minimal POM
The Minimal POM represents the bare minimum requirements for a POM. Its simplest structure includes:
project
- the project rootmodelVersion
- should be set to 4.0.0groupId
- the ID of the project groupartifactId
- the ID of the artifact (project)version
- the version of the project within the group
The pom.xml
looks like this:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
The POM mandates the configuration of groupId
, artifactId
, and version
. These three values form the fully qualified artifact name of the project, structured as <groupId>:<artifactId>:<version>
. In the example above, the fully qualified artifact name is com.mycompany.app:my-app:1
.
As mentioned earlier, Maven utilizes default values when specific configurations are not provided. One such default is the packaging type. Each Maven project has a packaging type, defaulting to jar
if not specified in the POM.
Notably, the Minimal POM does not specify repositories. If building a project with the Minimal POM, it inherits repository configurations from the Super POM. Thus, when Maven encounters dependencies in the Minimal POM, it knows to download them from the repository specified in the Super POM, typically https://repo.maven.apache.org/maven2
.
Project Inheritance
POM elements that can be inherited include:
- dependencies
- developers and contributors
- plugin lists (including reports)
- plugin executions with matching ids
- plugin configuration
- resources
While the Super POM serves as an example of project inheritance, you can introduce your own parent POM by specifying the parent element within your POM, as illustrated in the following examples.
Example 1
Scenario
Consider reusing the previous artifact com.mycompany.app:my-app:1
and introducing another artifact com.mycompany.app:my-module:1
.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>
Let’s specify their directory structure as follows:
.
|-- my-module
| `-- pom.xml
`-- pom.xml
Note: my-module/pom.xml
is the POM for com.mycompany.app:my-module:1
, while pom.xml
is the POM for com.mycompany.app:my-app:1
.
Solution
Now, to make com.mycompany.app:my-app:1
the parent artifact for com.mycompany.app:my-module:1
, we need to modify the POM for com.mycompany.app:my-module:1
as follows:
POM for com.mycompany.app:my-module:1
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>
Note that we’ve added a new section, namely the parent section. This section allows us to specify which artifact serves as the parent for the POM. To do so, we need to specify the fully qualified artifact name of the parent POM. With these settings, our module can inherit certain properties from the parent POM.
Additionally, if you wish the module’s groupId or version to match the parent module, you can omit the groupId or version tags in the module’s POM:
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<artifactId>my-module</artifactId>
</project>
This allows the module to inherit the groupId and version from its parent POM.
Example 2
Scenario
However, this approach is feasible only if the parent project is already installed in the local repository or structured in a specific directory hierarchy (i.e., the parent pom.xml is one level above the module’s pom.xml).
But what if the parent project is not yet installed and the directory structure is as follows?
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
Solution
To handle such directory structures (or any other structure), we must add a <relativePath>
element in the parent section.
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<artifactId>my-module</artifactId>
</project>
As the name suggests, it’s the relative path from the module’s `pom
.xml to the parent's
pom.xml`.
Project Aggregation
Project aggregation, similar to project inheritance, involves specifying modules from the parent POM rather than specifying a parent POM from the module. Thus, the parent project knows about its modules, and Maven commands executed for the parent project also apply to its modules. To aggregate projects, you need to:
- Change the
<packaging>
value in the parent POM topom
. - Specify the directories of its modules in the parent POM.
Example 3
Scenario
The original POM and directory structure are as follows:
POM for com.mycompany.app:my-app:1
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
POM for com.mycompany.app:my-module:1
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>
Directory Structure
.
|-- my-module
| `-- pom.xml
`-- pom.xml
Solution
To aggregate my-module
into my-app
, we simply modify my-app
:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>my-module</module>
</modules>
</project>
In the modified com.mycompany.app:my-app:1
, we’ve added the packaging
section and the modules
section. The packaging
is set to pom
, and the modules
section includes the element <module>my-module</module>
. The value of <module>
is the relative path from com.mycompany.app:my-app:1
to com.mycompany.app:my-module:1
’s POM (following convention, we use the artifactId of the module as the directory name).
Now, whenever Maven commands are executed for com.mycompany.app:my-app:1
, they’ll also be applied to com.mycompany.app:my-module:1
. Additionally, certain commands (especially goals) handle project aggregation differently.
Example 4
Scenario
But what if we change the directory structure to the following?
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
How does the parent POM specify the module?
Solution
The solution is similar to Example 3, specifying the module’s path:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<packaging>pom</packaging>
<modules>
<module>../my-module</module>
</modules>
</project>
Comparison between Inheritance and Aggregation
Inheritance: If you have multiple Maven projects with similar configurations, you can refactor your projects by extracting these common configurations and creating a parent project. Then, by making your Maven projects inherit from this parent project, these configurations will be applied across all projects.
Aggregation: If you have a set of projects that are built or handled together, you can create a parent project and declare these projects as its modules. Consequently, by building the parent project, the other projects will follow suit.
Using Inheritance and Aggregation together: Of course, you can also use project inheritance and project aggregation simultaneously. In other words, you can specify a parent project for your modules while also specifying those Maven projects as modules for the parent project. You just need to apply these three rules:
- Specify the parent POM in each child POM.
- Change the
<packaging>
value of the parent POM topom
. - Specify the directories of its modules in the parent POM.
Example 5
Scenario
com.mycompany.app:my-app:1’s POM
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
com.mycompany.app:my-module:1’s POM
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-module</artifactId>
<version>1</version>
</project>
Directory Structure
.
|-- my-module
| `-- pom.xml
`-- parent
`-- pom.xml
Solution
Applying the three rules, the solution is as follows:
com.mycompany.app:my-app:1’s POM
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<packaging>pom</packaging>
<!-- Change the packaging value to pom -->
<modules>
<module>../my-module</module>
</modules>
<!-- Declare the path of the module being aggregated -->
</project>
com.mycompany.app:my-module:1’s POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<!-- Declare the parent project being inherited, and if the parent project is not installed, declare its relative path -->
<artifactId>my-module</artifactId>
</project>
Note: Property inheritance follows the same strategy as inheritance used by the POM itself.
Project Interpolation and Variables
One of the practices Maven encourages is to avoid repetition. However, in certain cases, you may need to use the same value in multiple locations. To ensure that a value is specified only once, Maven allows you to use your own variables and predefined variables within the POM.
For example, to access the project.version variable, you can reference it like this:
<version>${project.version}</version>
One factor to note is that, as mentioned above, these variables are processed after inheritance. This means that if a variable is defined in the parent project and overridden in the child project, the child project will ultimately use its own definition rather than the parent project’s definition.
Available Variables
Project Model Variables
Any single value element field in the POM can be referenced as a variable. For example, ${project.groupId}
, ${project.version}
, ${project.build.sourceDirectory}
, and so on. Refer to the POM reference documentation for a complete list of properties.
These variables are referenced with the prefix project.
. You may also encounter references with the prefix pom.
or without any prefix; these forms are deprecated and should no longer be used.
Special Variables
project.basedir |
The directory where the current project resides. |
---|---|
project.baseUri |
The directory where the current project resides, represented as a URI. Since Maven 2.1.0 |
maven.build.timestamp |
Represents the timestamp when the build started (in UTC). Since Maven 2.1.0-M1 |
You can customize the format of the build timestamp by declaring the maven.build.timestamp.format
property:
<project>
...
<properties>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
</properties>
...
</project>
The format pattern must comply with the rules outlined in the SimpleDateFormat API documentation. If this property is not present, the format defaults to the value given in the example.