Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In a Maven multi-module project, the effective POM is the result of merging all configurations defined within a module and its parents.

In order to avoid redundancies and duplication between modules, we often keep common configurations in the shared parent. However, there can be a challenge if we need to have a custom configuration for a child module without impacting all its siblings.

In this tutorial, we'll learn how to override the parent plugin configuration.

2. Default Configuration Inheritance

Plugin configurations allow us to reuse a common build logic across projects. If the parent has a plugin, the child will automatically have it without additional configuration. This is like an inheritance.

To achieve this, Maven merges the XML files at the element level. If the child defines an element with a different value, it replaces the one from the parent. Let's see this in action.

2.1. Project Structure

First, let's define a multi-module Maven project to experiment on. Our project will consist of one parent and two children:

+ parent
     + child-a
     + child-b

Let's say we want to configure the maven-compiler-plugin to use different Java versions between modules. Let's configure our project to use Java 11 in general, but have child-a use Java 8.

We'll start with the parent configuration:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>11</source>
        <target>11</target>
        <maxmem>512m</maxmem>
    </configuration>
</plugin>

Here we've specified an additional property, maxmem, that we also want to use. However, we want child-a to have its own compiler settings.

So, let's configure child-a to use Java 8:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
</plugin>

Now that we have our example, let's see what this does to the effective POM.

2.2. Understanding the Effective POM

The effective POM is affected by various factors, like inheritance, profiles, external settings, and so on. In order to see the actual POM, let's run mvn help:effective-pom from the child-a directory:

mvn help:effective-pom

...
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <maxmem>512m</maxmem>
    </configuration>
</plugin>

As expected, child-a has its own variation of source and target values. However, one additional surprise is that it also has the maxmem property from its parent.

This means if any child property is defined, it will win, otherwise, the parent one will be used.

3. Advanced Configuration Inheritance

When we want to fine-tune the merging strategy, we can use attributes. These attributes are placed on the XML element that we want to control. Also, they will be inherited and will affect only the first-level children.

3.1. Working with Lists

In the previous example, we saw what happens if the child has a different value. Now, we'll see the case when the child has a different list of elements. As an example, let's look at including multiple resource directories, with the maven-resources-plugin.

As a baseline, let's configure the parent to include resources from the parent-resources directory:

<plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <configuration>
        <resources>
            <resource>
                <directory>parent-resources</directory>
            </resource>
        </resources>
    </configuration>
</plugin>

At this point, child-a will inherit this plugin configuration from its parent. However, let's say we wanted to define an alternative resource directory for child-a:

<plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <configuration>
        <resources>
            <resource>
                <directory>child-a-resources</directory>
            </resource>
        </resources>
    </configuration>
</plugin>

Now, let's review the effective POM:

mvn help:effective-pom
...
<configuration>
    <resources>
        <resource>
            <directory>child-a-resources</directory>
        </resource>
    </resources>
</configuration>

In this case, the entire list is overridden by the child configuration.

3.2. Append Parent Configuration

Perhaps we want some children to use a common resource directory and also define additional ones. For this, we can append the parent configuration by using combine.children=”append” on the resources element in the parent:

<resources combine.children="append">
    <resource>
        <directory>parent-resources</directory>
    </resource>
</resources>

Consequently, the effective POM will contain both of them:

mvn help:effective-pom
....
<resources combine.children="append">
    <resource>
        <directory>parent-resources</directory>
    </resource>
    <resource>
        <directory>child-a-resources</directory>
    </resource>
</resources>

The combine attributes are not be propagated to any nested elements. So, had the resources section been a complex structure, the nested elements would be merged using the default strategy.

3.3. Override Child Configuration

In the previous example, the child was not entirely in control of the final POM, owing to a parent combine strategy. A child can overrule the parent by adding combine.self=”override” on its resources element:

<resources combine.self="override">
    <resource>
        <directory>child-a-resources</directory>
    </resource>
</resources>

In this instance, the child regains control:

mvn help:effective-pom
...
<resources combine.self="override">
    <resource>
        <directory>child-a-resources</directory>
    </resource>
</resources>

4. Non-Inheritable Plugins

The previous attributes are suitable for fine-tuning, but they're not appropriate when the child completely disagrees with its parent.

To avoid a plugin being inherited at all, we can add the property <inherited>false</inherited> at the parent level:

<plugin>
    <inherited>false</inherited>
    <groupId>org.apache.maven.plugins</groupId>
    ...
</plugin>

This means the plugin will be applied only for the parent and will not be propagated to its children.

5. Conclusion

In this article, we saw how to override the parent plugin configuration.

First, we explored the default behavior. Then we saw how a parent can define a merging policy and how a child can reject it. Finally, we saw how to mark a plugin as non-inheritable to avoid all children having to override it.

As always, the code is available over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!