Maven, parents, aggregators and version update

In this blog post, I will describe a few scenarios where you want to update versions in a multi-module Maven project consistently. In these examples, you have both a parent POM, which is meant to contain common properties and configurations to be inherited throughout the other projects, and aggregator POMs, which are meant to be used only to build the multi-module project. Thus, the parent itself is NOT meant to be used to build the multi-module project.

The source code of the projects used in this post can be found here: https://github.com/LorenzoBettini/maven-versions-example.

I’m not saying that such a POM configuration and structure is ideal. I mean that it can be seen as a good practice to separate the concept of parent and aggregator POMs (though, typically, they are implemented in the same POM). However, in complex multi-module projects, you might want to separate them. In particular, as in this example, we can have different separate aggregator POMs because we want to be able to build different sets of children’s projects. The aggregators inherit from the parent to make things a bit more complex and interesting. Again, this is not strictly required, but it allows the aggregators to inherit the shared configurations and properties from the parent. However, this adds a few more (interesting?) problems, which we’ll examine in this article.

We’re going to use the standard Maven plugin’s goal: org.codehaus.mojo:versions-maven-plugin:set:

Description:
Sets the current project’s version and based on that change propagates that change onto any child modules as necessary.

This is the aggregator1 POM (note that it inherits from the parent and also mentions the parent as a child because we want to build the parent during the reactor, e.g., to deploy it):

Let’s make sure we can build it:

The aggregator2 is similar; here’s the build result:

Note that parent2 has parent1 as a parent:

Let’s say we want to update the version of parent1 consistently. Where do we run the following Maven command?

Let’s try to do that on the aggregator1:

Maybe on the parent1?

It looks like it worked… or it didn’t?

As you can see, it updated only the version of parent1, and now all the other children will refer to an old version. It didn’t work!

In fact, if we try to build the whole project with the aggregator1, it fails:

Before going on, let’s revert the change of version in parent1.

Let’s add references to aggregators in parent1

Let’s run the Maven command on parent1:

That makes sense: the aggregator has the parent as a child, and the parent has the aggregator as a child.

But what if we help Maven a little to detect all the children without a cycle?

It looks like it is enough to “hide” the references to children inside a profile that is NOT activated:

And the update works. All the versions are consistently updated in all the Maven modules:

The important thing is that aggregator2 does not have parent1 as a module (just parent2), or the Maven command will not terminate.

We can also consistently update the version of a single artifact; if the artifact is a parent POM, the references to that parent will also be updated in children. For example, let’s update only the version of parent2 by running this command from the parent1 project and verify that the versions are updated consistently:

Unfortunately, this is not the correct result: the version of parent2 has not been updated. Only the references to parent2 in the children have been updated to a new version that will not be found.

For this strategy to work, parent2 must have its version, not the one inherited from parent1.

Let’s verify that: let’s manually change the version of parent2 to the one we have just set in its children:

And let’s try to update to a new version the parent2:

Nothing has changed… it did not work.

Let’s try and be more specific by specifying the old version (after all, we’re running this command from parent1 asking to change the version of a specific child):

This time it worked! It updated the version in parent2 and all the children of parent2.

Let’s reset all the versions to the initial state.

Let’s remove the “hack” of child modules from parent1 and create a brand new aggregator that does not inherit from any parent (in fact, it configures the versions plugin itself) but serves purely as an aggregator:

Let’s try to run the version update from this aggregator:

It updated the version of the aggregator only! That’s not what we want.

Let’s revert the change.

We know that we can use the artifactId

What if the same child is included in our aggregators aggregator1 and aggregator2? For example:

We get an error if we try to update the version as above because the same module is present twice in the same reactor:

But what if we apply the same trick of the modules inside a profile in this new aggregator project, which is meant to be used only to update versions consistently?

For example,

This time, the version update works even when the same module is present in both our aggregator1 and aggregator2! Moreover, versions are updated only once in the module mentioned in both our aggregators:

Maybe, this time, this is not to be considered a hack because we use this aggregator only as a means to keep track of version updates consistently in all the children of our parent POMs.

As I said, these might be seen as complex configurations; however, I think it’s good to experiment with “toy” examples before applying version changes to real-life Maven projects, which might share such complexity.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.