Blog

  • 2024
  • 2023
  • 2022
  • 2021
  • 2020
  • 2019
  • 2018
  • 2017
  • 2016
  • 2015
  • 2014
  • 2013
  • 2012

In this article we will discuss the use of parent and aggregate POMs and some build parameters provided by Maven to make building large projects with Maven quite easy.

We also want to stress the notion of a release cycle and the consequences on versioning, if you build your products with Maven.

Definitions

Let’s start with the definition of aggregate and parent POM given in Create an Aggregate Maven Project by authors from Red Hat:

Aggregate POM

A complete application typically consists of multiple Maven projects. As the number of projects grows larger, however, it becomes a nuisance to build each project separately. Moreover, it is usually necessary to build the projects in a certain order and the developer must remember to observe the correct build order.

To simplify building multiple projects, you can optionally create an aggregate Maven project. This consists of a single POM file (the aggregate POM), usually in the parent directory of the individual projects. The POM file specifies which sub-projects (or modules) to build and builds them in the specified order.

Create an Aggregate Maven Project

Parent POM

Maven also supports the notion of a parent POM. A parent POM enables you to define an inheritance style relationship between POMs. POM files at the bottom of the hierarchy declare that they inherit from a specific parent POM. The parent POM can then be used to share certain properties and details of configuration. Create an Aggregate Maven Project

Multi-Module Projects

If you have a multi-module project, building all subprojects at once is a trivial task. In the parent POM you add a module element in the modules section for all child projects and you are done.

<project ...>
  ...
  <groupId>de.mycorp.something</groupId>
  <artifactId>something</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  ...
  <modules>
    <module>something-lib</module>
    <module>something-domain</module>
    <module>something-web</module>
  </modules>
  ...
</project>

The POM is an aggregate POM since it references modules. The submodules reference this POM as their parent, what makes the aggregate POM also a parent POM.

<project ...>
  ...
  <parent>
    <groupId>de.mycorp.something</groupId>
    <artifactId>something</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>
  ...
  <artifactId>something-web</artifactId>
  ...
</project>

This is a special case where a POM is a parent and at the same time also an aggregate POM.

In POM Best Practices you can read about best practices published by Sonatype for some good examples, in that they give detailed discussion on dependencies between projects. But what, in our point of view, falls short in these examples, are the concepts of versioning and release lifecycle, which also play a major role in deciding how to structure your projects.

Versioning

What you can see in the module POMs example is that it inherits its version from the parent POM. This is quite natural for small to medium sized projects. The team makes code changes on every module for the next release.

You may use different versions for your modules, but this will not take you very far. If you release the project via its aggregate parent POM, all modules will get released, with their individual version. That’s fine – at first thought. If you look closer you will notice that the version will increase, even if some of the modules have not any changes at all. With this approach, having a multi-module aggregate parent POM using different versions for each submodule, you only have the flexibility to decide, if a new version is major, minor or micro. But is this worth the effort?

Release Cycle

In most simple scenarios you will have less problems, if you let inherit your submodules the parent projects version and release your whole project in one go. We think that wanting different versions is an indicator that the module should reside in their own release cycles. The evidence becomes even stronger, if the module is used by other projects as well. In this case you are usually far better off with some individual projects you build and release on their own. This is because it may become quite cumbersome, if you want to make a release of a submodule without making a release for the whole multi-module project.

The authors of Create an Aggregate Maven Project come to a similar solution:

Quite often, you will see examples where a POM is used both as a parent POM and an aggregate POM. This is acceptable for small, relatively simple applications, but is not recommended as best practice. Best practice is to define separate POM files for the parent POM and the aggregate POM.

This meets our observation: In larger projects a team tries to reduce unnecessary build by refactoring modules into their own projects with their own release cycle. A monolithic build is often easy to handle at the start of a project, where the number of modules is small. By calling whatever build command at the root project, the whole project gets built in a few minutes (if you do not build a site with plenty of reports). As soon as the number of projects increases, it gets more and more cumbersome due to the increased build time.

Want to release all at once nonetheless?

Some may argue that they want to

        build the whole project with a single click of a button.

But we think that is only the case for small projects. What is really needed is that

        the product can be build with a single click of a button.

That does not include that every dependency is also built with the product at the same instance of time. The dependencies usually have been built prior and are stored in an artifact repository for quick access. This is true for all external dependencies and is also true for dependencies to artifacts that have been build by your corporation.

I want to build all Project Modules nonetheless!

A developer often needs to check out some source code of the project and get this whole thing compiled and tested before starting to work. And this is what aggregate POMs are designed for, since they resolve all referenced modules in the reactor and thereby calculate the correct build order for all projects.

Alternatives?

 

The Maven Invoker Plugin allows you to start a build on a given project. But this plugin is not designed to do the reactor resolvement. With other words: You cannot control the order of projects to be run. So this plugin, although a very helpful tool to solve other problems, is no alternative for our scenario.

Let’s assume that the project has three modules where the library, the domain, and the web project are released independently. The reason for this is that the library and the domain are also used by other products and may even be build by other teams.

This is what the aggregate POM looks like:

<project ...>
  ...
  <groupId>de.mycorp.something</groupId>
  <artifactId>something</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  ...
  <modules>
    <module>something-pom</module>
    <module>something-lib</module>
    <module>something-domain</module>
    <module>something-web</module>
  </modules>
  ...
</project>

The aggregate POM is no longer the parent POM – there is a new submodule that has this responsibility.

<project ...>
  ...
  <parent>
    <groupId>de.mycorp.something</groupId>
    <artifactId>something-pom</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>
  ...
  <artifactId>something-web</artifactId>
  ...
</project>

The positive effect of this approach is that the modules can be build together while they still have their individual release cycles. The aggregate POM is just a tool for the developer to build a defined bunch of projects.

Limitations

In the past we encountered some difficulties in building projects alone or as part of multi-module-builds. But with Maven 3 these problems seemed to be history. In his article Top Ten Reasons to Move to Maven 3 Anders Hammar explains that Maven 3 fixes a couple of dependency problems in reactor builds:

#2. Maven 3 Isolates Project Dependencies and Plugin Dependencies

#5. Maven 3 has Improved Artifact/Dependency Resolution

Thanks to the refactoring of the artifact resolution logic in Maven, the existing deficiencies of artifact resolution in reactor builds with Maven 2.x have been fixed.

#7. Maven 3 has More Intelligent Class Loading for Multi-module Builds

Some problems still exist, though:

  1. MNG-3559: Problems building a site, if test sources are attached.
    As a workaround call mvn install site

Using Profiles

In his stackoverflow answer to the question Maven Aggregate POM with Goal? Martin Algesten supposes to use profiles to invoke different builds.

Here is an example that shows that this tip is quite handy:

<project ...>
  ...
  <profiles>
    <profile>
      <id>backend</id>
      <modules>
        <module>something-lib</module>
        <module>something-domain</module>
      </modules>
    </profile>
    <profile>
      <id>frontend</id>
      <modules>
        <module>something-web</module>
      </modules>
    </profile>
    <profile>
      <id>all</id>
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
      <modules>
        <module>something-pom</module>
        <module>something-lib</module>
        <module>something-domain</module>
        <module>something-web</module>
      </modules>
    </profile>
  </profiles>
  ...
</project>

In our example we could build all modules at once,

mvn clean install

build only the backend

mvn clean install -Pbackend

or the frontend

mvn clean install -Pfrontend

part.

This way you do not have to really build the whole project, but you can selectively build those parts you are currently working on.

Helpers on the command line

Since version 2.1 Maven provides some very useful command line options to interact with the reactor. Most people use the -rf option to continue the build with the given project after fixing some issues that made a multi-module build fail.

Using an aggregate POM we are also able to build a specified project and all projects in the reactor it depends on:

mvn clean install --projects something-domain -am -Pbackend

This will build the domain module and the library module.

 

If you are using our Alias Maven Plugin, the call would get shorter ;-):

i --projects something-domain -am -Pbackend

With -amd you may run the opposite build which builds the specified project and all projects in the reactor that depend on this project. For detailed information on the possibilities working with the reactor please refer to Maven Tips and Tricks: Advanced Reactor Options by Tim O’Brien.

Using a batch POM

Batch POM Project

If you do not want to have the aggregate POM contain the submodule folders, you may add a dedicated batch POM to your sub modules like this

  • something-pom
  • something-batch-pom
  • something-lib
  • something-domain
  • something-web

The advantage of this approach is there is no aggregate POM file in the parent directory that could get confused with a parent POM of a multi-module project.

The contents of the batch POM looks like this:

<project ...>
  ...
  <artifactId>something-batch-pom</artifactId>
  <version>0.1.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  ...
  <profiles>
    ...
    <profile>
      <id>all</id>
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
      <modules>
        <module>../something-pom</module>
        <module>../something-lib</module>
        <module>../something-domain</module>
        <module>../something-web</module>
      </modules>
    </profile>
  </profiles>
  ...
</project>

But this is not quite the Maven way to look at it. You can see this as soon as you want to refer to the module from the command line. The name of the module is ../something-domain, not something-domain, which is somewhat unwieldy. But it is necessary to identify the referenced modules uniquely.

mvn clean install --projects ../something-domain -am -Pbackend

Using a batch POM File

If you do not want either of these options, there is a third one: Add a POM file by the name batch-pom.xml to the parent folder and call this one explicitly from the command line

mvn -f batch-pom.xml

The module names can be used as expected for all modules within the folder and the POM file’s name clearly states the reason for its existence. The drawback is that you have to specify the name of the POM file explicitly.

Conclusion

This article provided some easy guidelines to work with parent and aggregate POMs:

  1. If your parent POM is also the aggregate POM, all projects are released simultaneously.
  2. A parent POM (that is not an aggregate POM) is released in its own cycle and owns an individual version number.
  3. An aggregate POM (that is not a parent POM) is never released and only acts as a build helper.
    1. Use profiles to bundle projects
    2. Use commandline options to build only part of the projects in the reactor.


User Documentation for Maven Projects?

 

The projectdoc Toolbox for Confluence supports agile team in their task to communicate with stakeholders.

For instance developers need to

For all of that the projectdoc Toolbox provides macros and doctypes (also know as templates or blueprints) to make these tasks much easier.

Quick Start to learn projectdoc in 4 Steps!
More than a short introduction, this tutorial introduces the need-to-know basics to get started with projectdoc.
How to document a Software Development Project
There is no one-size-fits-all for documenting software projects. What we do is giving you an introduction on how to get started with the projectdoc Toolbox and the Software Development Add-on to define your documentation requirements with Confluence.
Use Cases
An overview over the use cases for which the projectdoc Toolbox provides support.
projectdoc Toolbox Online Manual
The online manual for version 1 of the projectdoc Toolbox for Confluence.
Tips
List of tips to use Confluence with the projectdoc Toolbox and fun! Tips address users of different experience levels.

The library smartics exception for Java has been released with version 0.12.1.

This is a bugfix release that fixes a problem in the ognl library by updating from version 2.7.1 to 2.7.3. The library in the previous version had problems analysing certain classes, e.g. JSONException. For details on this issue please refer to Bug 759.

For details on this version of the library please visit the project's homepage, for changes since the last version, please consult the release report.

The library smartics exception for Java has been released with version 0.12.0.

This release adds a new feature that allows methods for message parameters to create locale dependent representations. Such a method simply adds a parameter of type java.util.Locale and gets the locale passed from the calling framework. Here is an example of a method that generates the replacement for a message parameter that is dependent on the locale:

@MessageParam("violations")
public final String getViolationsAsText(final Locale locale) {
  final StringBuilder buffer = new StringBuilder();
  if (propertyViolations != null && !propertyViolations.isEmpty()) {
    for (final PropertyException violation : propertyViolations) {
      buffer.append("  ")
            .append(violation.getLocalizedMessage(locale))
            .append('\n');
    }
  }
  return StringUtils.chomp(buffer.toString());
} 

A second new feature allows to call the locale-aware message getter on the cause of an I18N smartics exception. Prior to this the getLocalizedMessage had been called that used the system’s default locale, which usually did not return the desired representation.

For details on this version of the library please visit the project’s homepage, for changes since the last version, please consult the release report.

JDOM Dependencies

 This short post describes two dependency issues when dealing with the JDOM library.

  1. Working with an older Xerces version
  2. Mixing two versions of the JDOM library

JDOM 2 and ExceptionInInitializerError

The following configuration shows dependencies on JDOM in version 2.0.2 with a Xerces parser version smaller than 2.7:

<dependency>
  <groupId>org.jdom</groupId>
  <artifactId>jdom</artifactId>
  <version>2.0.2</version>
</dependency>
<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.6.2</version>
</dependency>

Using this configuration will produce the following stacktrace:

java.lang.ExceptionInInitializerError
	at org.jdom2.input.SAXBuilder.<init>(SAXBuilder.java:338)
	at org.jdom2.input.SAXBuilder.<init>(SAXBuilder.java:221)
        ...
Caused by: java.lang.UnsupportedOperationException: This parser does not support specification "null" version "null"
	at javax.xml.parsers.SAXParserFactory.setSchema(SAXParserFactory.java:419)
	at org.jdom2.input.sax.XMLReaders.<init>(XMLReaders.java:122)
	at org.jdom2.input.sax.XMLReaders.<clinit>(XMLReaders.java:95)
	... 27 more

To get rid of this problem, simply use a modern version of the libraries:

<dependency>
  <groupId>org.jdom</groupId>
  <artifactId>jdom2</artifactId>
  <version>2.0.4</version>
</dependency>
<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.11.0</version>
</dependency>

Please note that the artifact identifier of the JDOM library is jdom2.

Also note that there is no problem if you use

<dependency>
  <groupId>org.jdom</groupId>
  <artifactId>jdom</artifactId>
  <version>2.0.2</version>
</dependency>

without an older Xerces version. So the problem described above usually only arises, if an older Xerces version leaks in by a transitive dependency.

JDOM1 side by side with JDOM2

It is no problem if you have JDOM in version 1 and 2 on your classpath. For one this is due to the fact that the designers of this library updated the package structure. But users have to make sure to employ two different artifacts. In this case Maven does not resolve to only one of them.

So this will work as expected (different artifact IDs):

<dependency>
 <groupId>org.jdom</groupId>
 <artifactId>jdom2</artifactId>
 <version>2.0.4</version>
</dependency>
<dependency>
 <groupId>org.jdom</groupId>
 <artifactId>jdom</artifactId>
 <version>1.1.3</version>
</dependency>

And this one, using artifact jdom instead of jdom2, does not:

<dependency>
 <groupId>org.jdom</groupId>
 <artifactId>jdom</artifactId>
 <version>2.0.2</version>
</dependency>
<dependency>
 <groupId>org.jdom</groupId>
 <artifactId>jdom</artifactId>
 <version>1.1.3</version>
</dependency>

In the latter case, only one of the artifacts will be on the resolved classpath, class cast exceptions like this will be encountered:

Caused by: java.lang.ClassNotFoundException: org.jdom.JDOMException