Skip to content

Conversation

@gnodet
Copy link
Contributor

@gnodet gnodet commented Jul 20, 2023

Needs to be rebased on top of #2475

Maven POM Mixins: Enhancing Project Composition

Overview

Maven 4.1.0 introduces POM Mixins, a powerful new feature that allows for more flexible and modular project composition. Mixins enable you to extract common configurations into reusable components that can be included in your projects, promoting better organization and reducing duplication across your build configurations.

What are Mixins?

Mixins are reusable POM fragments that can be included in your project, similar to parent POMs but with more flexibility. Unlike parent POMs which establish a strict hierarchy, mixins allow for composition from multiple sources without inheritance constraints. This enables a more modular approach to build configuration.

Key Benefits

  • Modularity: Extract common configurations into reusable components
  • Flexibility: Include multiple mixins in a single project
  • Reduced Duplication: Define configurations once and reuse them across projects
  • Simplified Maintenance: Update configurations in one place

Usage Examples

Including a Mixin by Path

Mixins can be included from the file system using a relative path:

<project xmlns="http://maven.apache.org/POM/4.2.0">
  <modelVersion>4.2.0</modelVersion>
  <groupId>org.example</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0.0</version>
  
  <mixins>
    <mixin>
      <relativePath>mixins/mixin-1.xml</relativePath>
    </mixin>
  </mixins>
</project>

Including a Mixin by GAV Coordinates

Mixins can also be retrieved from repositories using standard Maven coordinates:

<mixins>
  <mixin>
    <groupId>org.example</groupId>
    <artifactId>my-mixin</artifactId>
    <version>1.0.0</version>
  </mixin>
</mixins>

Using Mixins with Classifiers

For more specialized configurations, mixins can be referenced with classifiers:

<mixins>
  <mixin>
    <groupId>org.example</groupId>
    <artifactId>my-mixin</artifactId>
    <version>1.0.0</version>
    <classifier>special</classifier>
  </mixin>
</mixins>

Compatibility

Mixins are only available in Maven 4.1.0 and later, corresponding to modelVersion 4.2.0 or higher. Projects using mixins must specify <modelVersion>4.2.0</modelVersion> in their POM.

Implementation Details

The mixins feature extends the Maven model by adding a new <mixins> element that can contain multiple <mixin> declarations. Each mixin can be specified either by a relative path or by GAV coordinates, optionally with a classifier.

When Maven processes a project with mixins, it resolves each mixin, validates it, and merges its contents into the project model according to well-defined merging rules, similar to how parent POMs are processed but with important differences in precedence and inheritance behavior.

@gnodet gnodet marked this pull request as draft July 20, 2023 06:03
@gnodet gnodet force-pushed the mixins branch 3 times, most recently from a810482 to f97c405 Compare July 21, 2023 19:32
@gnodet gnodet force-pushed the mixins branch 2 times, most recently from 81f4422 to 211e27a Compare August 4, 2023 06:38
@gnodet gnodet force-pushed the mixins branch 2 times, most recently from 1570e7f to 4c87f2a Compare August 23, 2023 14:22
@gnodet gnodet force-pushed the mixins branch 4 times, most recently from c2b6ec7 to e4cd448 Compare September 13, 2023 12:01
@gnodet gnodet added this to the 4.0.0-alpha-8 milestone Sep 14, 2023
@gnodet gnodet modified the milestones: 4.0.0-alpha-8, 4.0.0 Sep 26, 2023
@gnodet gnodet modified the milestones: 4.0.0, 4.x Jul 6, 2024
<mixins>
<mixin>
<groupId>org.apache.maven.its.mng5102</groupId>
<artifactId>mixin-2</artifactId>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this take a classifier? One of the early issues we identified with tiles - which is why we have a pom.xml and a tile.xml - is for publishing to a repo.

The pom.xml would have release plugin information, which tne tile/mixin wouldn't want.

I like what I'm seeing here, and it looks like it should support my own needs - will try find some time to pull/build this locally and give it a renewed test.

Copy link
Contributor Author

@gnodet gnodet Mar 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's now supported !

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great news. I looked at the tests quickly and it seemed like the mixins there only contains properties.

What can be included (or not included) in the mixins?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mixins are merged in their declaration order as if they were parents, so there are no restrictions at this point. I haven't given much thought if there should be yet...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@talios Our organization use your Maven Tiles (thank you!). Trying to think if there is something in Maven Tiles that this current implementation of Maven Mixins does not support. With Maven Tiles we have issues with profiles (the are active but Maven does not alwats acknowledge them) and properties (properties aren't propagated).

From what Guillaume wrote it seems as if mixins, currently not having any restrictions, might function even better that Maven Tiles.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimisola Depending on how this works in practise - and @gnodet may be able to quickly confirm, one big thing that users of tiles seem to love (which is something I'd gladly give up as I don't overlike it) - is having mixins, that contain mixins.

Tiles also (by default) disallows dependencies (unlocked via the code smells configuration) - but it's often useful to include runtime dependencies to match plugins.

Say an antlr mixin might declared the antlr runtime dep, AND the plugin config.

One thing I don't see here (and also only cause I've not yet had a running version work yet) is logging of mixins being pulled in. That's quite handy for debugging, as is our default behavior of rewriting the execution ids to be prefixed by the mixin.

The rewriting of execution ids could easily just be done IN the mixin, with descriptive ids to begin with tho.

Properties aren't propagated? AFAIK they are as we use them often, the issue we have (which is possible more to Model inheritance) is if a tile references a property, but doesn't declare it a default value THEN it doesn't seem to work.

But having a property defined at the mixin/tile level, and overridden in the top level pom works.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it is of interest for @gnodet but here is our Maven Tiles setup.

We use tiles and composites where are used to group tiles.

Hence, we have a need for including mixins in mixins for us to be able to group mixins into composites.

Parent Tile

  • maven-tiles-parent which is used to set the Maven Tiles version for all tiles (won't be needed with Maven Mixins)

Tiles:

  • common-tile
  • java-17-tile
  • java-21-tile
  • lfv-development-environment-tile
  • lint-tile
  • maven-core-versions-tile
  • openapi-tile
  • opentelemetry-tile
  • reqstool-tile
  • soap-tile
  • spring-boot-amqp-tile
  • spring-boot-application-tile
  • spring-boot-camel-tile
  • spring-boot-mvc-tile
  • spring-boot-oauth-tile
  • spring-boot-redis-tile
  • spring-boot-tile
  • spring-boot-vault-tile
  • sysdev-common-tile
  • test-compile-tile

@talios As for the properties issue (our use case seem to be different), with respect for this PR we refer to this new ticket in Maven Tiles.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimisola I saw that ticket, I see what you mean - I always propagate UP into the tile (so overrides in the project).

I've never really been a fan of composites (which are usually dependencies) as BOM imports already exist - composites just remove the need to declare each dependency in each module - which, in our application which is OSGi based can cause issues if we're just adding deps unseen - I guess it also depends on how often you churn releases of tiles, or dependencies ( we use an additional tool I wrote for $work that handles version ranges/lock files) to track and check that.

Out of interesting - do you tiles include dependencies, or just plugins?

Copy link

@lfvjimisola lfvjimisola Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@talios Both dependencies and plugins.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally got around to testing this (it's been a hectic fortnight). From a fresh archetype:generated project, and updating the model version and XML preamble, adding the following maven tile "mixin":

<mixins>
  <mixin>
    <groupId>com.smxemail.tiles</groupId>
    <artifactId>com.smxemail.tiles.enforcements</artifactId>
    <version>4.0.176</version>
    <extension>xml</extension>
  </mixin>
</mixins>

Everything seemed to work as expected!

Compared to the tiles config:

<plugin>
  <groupId>io.repaint.maven</groupId>
  <artifactId>tiles-maven-plugin</artifactId>
  <version>2.40</version>
  <extensions>true</extensions>
  <configuration>
    <tiles>
      <tile>com.smxemail.tiles:com.smxemail.tiles.enforcements:[4.0.176]</tile>
    </tiles>
  </configuration>
</plugin>

I long for the day when we can have some more concise GAV specifications in Maven, but this is looking good as a transition.

The tile does transclude another tile, which obviously doesn't work here. I will craft one with its own <mixin> element and do some further testing.

@gnodet gnodet modified the milestones: 4.x, 4.1.0 Apr 4, 2025
@jimisola
Copy link

jimisola commented Apr 29, 2025

One challenge we have with Maven Tiles is that plugin configurations for the same plugin will overwrite each other. How is this handled with mixins?

@gnodet
Copy link
Contributor Author

gnodet commented Apr 29, 2025

One challenge we have with Maven Tiles is that plugin configurations for the same plugin will overwrite each other. How is this handled with mixins?

They use the same mechanism as parents, so it will be like if one plugin definition is in the parent and the other one in the grand-parent. They end up being merged. The order will have to be clearly specified though.

@lfvjimisola
Copy link

One challenge we have with Maven Tiles is that plugin configurations for the same plugin will overwrite each other. How is this handled with mixins?

They use the same mechanism as parents, so it will be like if one plugin definition is in the parent and the other one in the grand-parent. They end up being merged. The order will have to be clearly specified though.

When you say merged - in what way?

A (parent):

    <build>
        <plugins>
            <plugin>
                <groupId>plugin</groupId>
                <artifactId>plugin</artifactId>
                <configuration>
                    <a>a</a>
                    <c>a</c>
                </configuration>
            </plugin>
        </plugins>
    </build>

B (child)

    <build>
        <plugins>
            <plugin>
                <groupId>plugin</groupId>
                <artifactId>plugin</artifactId>
                <configuration>
                    <b>b</b>
                    <c>b</c>
                </configuration>
            </plugin>
        </plugins>
    </build>

What would be the outcome of the above?

  1. The entire plugin is configured by A or B depending on order
  2. Everything is merged but A has priority so
    a
    b
    a
  3. Everything is merged but B has priority so
    a
    b
    b
  4. other outcome

@gnodet
Copy link
Contributor Author

gnodet commented Apr 29, 2025

One challenge we have with Maven Tiles is that plugin configurations for the same plugin will overwrite each other. How is this handled with mixins?

They use the same mechanism as parents, so it will be like if one plugin definition is in the parent and the other one in the grand-parent. They end up being merged. The order will have to be clearly specified though.

When you say merged - in what way?

A (parent):

    <build>
        <plugins>
            <plugin>
                <groupId>plugin</groupId>
                <artifactId>plugin</artifactId>
                <configuration>
                    <a>a</a>
                    <c>a</c>
                </configuration>
            </plugin>
        </plugins>
    </build>

B (child)

    <build>
        <plugins>
            <plugin>
                <groupId>plugin</groupId>
                <artifactId>plugin</artifactId>
                <configuration>
                    <b>b</b>
                    <c>b</c>
                </configuration>
            </plugin>
        </plugins>
    </build>

What would be the outcome of the above?

  1. The entire plugin is configured by A or B depending on order
  2. Everything is merged but A has priority so
    a
    b
    a
  3. Everything is merged but B has priority so
    a
    b
    b
  4. other outcome

Maven identifies plugins by their groupId and artifactId. Since both parent and child define the same plugin, their configurations are merged.

Configuration Merging:

For non-conflicting elements (elements present in one POM but not the other), Maven includes both elements in the final configuration.
For conflicting elements (elements with the same name in both parent and child), the child's configuration takes precedence and overrides the parent's configuration.
By default, Maven does not append or merge the content of elements unless explicitly instructed (e.g., using combine.children="append").

Non-conflicting elements:

Parent has a, which is not present in the child. This element is included in the final configuration.
Child has b, which is not present in the parent. This element is included in the final configuration.

Conflicting elements:

Both parent and child define . The parent has a, and the child has b.
The child's b overrides the parent's a.

Resulting Configuration:

The merged configuration for the plugin in the child project will include:

<a>a</a> (from parent, non-conflicting).
<b>b</b> (from child, non-conflicting).
<c>b</c> (from child, overriding parent's <c>a</c>).

Final Outcome
The effective configuration for the plugin in the child project (B) will be:

<build>
    <plugins>
        <plugin>
            <groupId>plugin</groupId>
            <artifactId>plugin</artifactId>
            <configuration>
                <a>a</a>
                <b>b</b>
                <c>b</c>
            </configuration>
        </plugin>
    </plugins>
</build>

Notes

If you want to append configurations (e.g., combine <c> values from parent and child), you can use the combine.children="append" attribute in the child POM. For example:

<configuration combine.children="append">
    <b>b</b>
    <c>b</c>
</configuration>

This would require a more complex structure for <c> (e.g., as a list), which depends on the plugin’s configuration schema.
If you want to prevent inheritance of the parent’s configuration, you can add <inherited>false</inherited> to the child’s plugin definition:

<plugin>
    <groupId>plugin</groupId>
    <artifactId>plugin</artifactId>
    <inherited>false</inherited>
    <configuration>
        <b>b</b>
        <c>b</c>
    </configuration>
</plugin>

This would result in only <b>b</b> and <c>b</c> in the child’s configuration, ignoring <a>a</a> and <c>a</c> from the parent.

@jimisola
Copy link

@gnodet Thank you for the very thorough description with examples.

Been using Maven for decades (literally!) and I just realized that I've never seen the advanced configuration inheritance with combine.children and combine.self.

Really looking forward to mixins in Maven 4.

@gnodet gnodet marked this pull request as ready for review April 30, 2025 11:17
@gnodet gnodet force-pushed the mixins branch 4 times, most recently from 444061b to 9546305 Compare June 12, 2025 20:21
@jira-importer
Copy link

Resolve #6814

@jimisola
Copy link

@gnodet Will this be included in Maven 4 (4.0.0-rc and final)?

@gnodet
Copy link
Contributor Author

gnodet commented Jul 21, 2025

@gnodet Will this be included in Maven 4 (4.0.0-rc and final)?

We're trying to freeze 4.0, so it's scheduled for 4.1.

@jimisola
Copy link

@gnodet Will this be included in Maven 4 (4.0.0-rc and final)?

We're trying to freeze 4.0, so it's scheduled for 4.1.

Ok. That was unfortunate but totally understandable. Good luck with 4.0.0!

@gnodet gnodet force-pushed the mixins branch 2 times, most recently from 16ab167 to 014b2cc Compare July 24, 2025 13:27
@gnodet gnodet changed the title [MNG-5102] Add support for POM mixins Add support for POM mixins Jul 24, 2025
This commit implements Maven Mixins, a powerful mechanism for sharing
common POM configuration across multiple projects without the limitations
of traditional inheritance.

Key features:
- Compose project configuration from multiple sources
- Overcome single inheritance limitation
- Reduce configuration duplication
- Enable better separation of concerns

Changes include:
- New model version 4.2.0 support for mixins
- Mixin and mixinManagement elements in POM model
- Mixin resolution and composition logic
- Integration with existing inheritance system
- Comprehensive documentation and examples
- Maven 3 compatibility fixes for maven.config

The implementation allows projects to declare mixins that are resolved
and merged in order, with later mixins overriding earlier ones, and
the current POM having final precedence.
@gnodet gnodet merged commit 99447f1 into apache:master Aug 7, 2025
19 checks passed
@github-actions
Copy link

github-actions bot commented Aug 7, 2025

@gnodet Please assign appropriate label to PR according to the type of change.

@lfvjimisola
Copy link

@gnodet Is there pre-build of Maven 4.1 somewhere that can be used to test Maven Mixins?

When do you think it is likely that 4.1 will be released?

@gnodet gnodet deleted the mixins branch September 2, 2025 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants