Skip to content

Commit 444061b

Browse files
committed
[MNG-5102] Add support for POM mixins
Maven 4.1.0 introduces POM Mixins, a powerful new feature that enables more flexible and modular project composition. Mixins allow extracting common configurations into reusable components that can be included in projects, promoting better organization and reducing duplication across build configurations. Key Features: - Modular composition: Extract common configurations into reusable components - Multiple inclusion methods: Include mixins by relative path or GAV coordinates - Classifier support: Reference specialized configurations with classifiers - No inheritance constraints: Unlike parent POMs, mixins allow composition from multiple sources Usage Examples: - Path-based: <relativePath>mixins/mixin-1.xml</relativePath> - GAV-based: <groupId>org.example</groupId><artifactId>my-mixin</artifactId><version>1.0.0</version> - With classifier: <classifier>special</classifier> Implementation Details: - Introduces modelVersion 4.2.0 to support mixins functionality - Extends Maven model with new <mixins> element containing <mixin> declarations - Integrates mixin resolution and merging into model building process - Maintains well-defined merging rules similar to parent POM processing - Includes comprehensive integration tests for all usage scenarios Technical Changes: - Added MODEL_VERSION_4_2_0 constant and updated VALID_MODEL_VERSIONS - Enhanced DefaultModelBuilder with mixin processing logic - Updated model schema (maven.mdo) to include mixins elements - Added POM schema files for model version 4.2.0 - Integrated mixin inheritance assembly and validation - Added comprehensive test suite covering path, GAV, and classifier scenarios Compatibility: - Requires Maven 4.1.0+ and modelVersion 4.2.0 or higher - Backward compatible with existing POM structures - Consumer POMs properly handle mixin elements for downstream compatibility Resolves: https://issues.apache.org/jira/browse/MNG-5102
1 parent 276f9e7 commit 444061b

File tree

26 files changed

+834
-264
lines changed

26 files changed

+834
-264
lines changed

api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ public interface ModelBuilder extends Service {
2929

3030
String MODEL_VERSION_4_1_0 = "4.1.0";
3131

32-
List<String> KNOWN_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0);
32+
String MODEL_VERSION_4_2_0 = "4.2.0";
33+
34+
List<String> VALID_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0, MODEL_VERSION_4_2_0);
3335

3436
ModelBuilderSession newSession();
3537

api/maven-api-model/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ under the License.
4848
<groupId>org.codehaus.modello</groupId>
4949
<artifactId>modello-maven-plugin</artifactId>
5050
<configuration>
51-
<version>4.1.0</version>
51+
<version>4.2.0</version>
5252
<velocityBasedir>${project.basedir}/../../src/mdo</velocityBasedir>
5353
<models>
5454
<model>src/main/mdo/maven.mdo</model>

api/maven-api-model/src/main/mdo/maven.mdo

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@
111111
</association>
112112
</field>
113113

114+
<!-- ====================================================================== -->
115+
<!-- Mixins -->
116+
<!-- ====================================================================== -->
117+
118+
<field xdoc.separator="blank">
119+
<name>mixins</name>
120+
<version>4.2.0+</version>
121+
<description>Mixins...</description>
122+
<association>
123+
<type>Mixin</type>
124+
<multiplicity>*</multiplicity>
125+
</association>
126+
</field>
127+
114128
<!-- ====================================================================== -->
115129
<!-- groupId/artifactId/Version/Packaging -->
116130
<!-- ====================================================================== -->
@@ -526,7 +540,7 @@
526540
<fields>
527541
<field xdoc.separator="blank">
528542
<name>modules</name>
529-
<version>4.0.0/4.1.0</version>
543+
<version>4.0.0/4.2.0</version>
530544
<description>
531545
@deprecated Use {@link #subprojects} instead.
532546
</description>
@@ -540,7 +554,7 @@
540554
</field>
541555
<field xdoc.separator="blank">
542556
<name>subprojects</name>
543-
<version>4.1.0</version>
557+
<version>4.1.0+</version>
544558
<description>The subprojects (formerly called modules) to build as a part of this
545559
project. Each subproject listed is a relative path to the directory containing the subproject.
546560
To be consistent with the way default URLs are calculated from parent, it is recommended
@@ -1836,6 +1850,23 @@
18361850
</codeSegments>
18371851

18381852
</class>
1853+
<class>
1854+
<name>Mixin</name>
1855+
<version>4.1.0+</version>
1856+
<superClass>Parent</superClass>
1857+
<fields>
1858+
<field>
1859+
<name>classifier</name>
1860+
<version>4.1.0+</version>
1861+
<type>String</type>
1862+
</field>
1863+
<field>
1864+
<name>extension</name>
1865+
<version>4.1.0+</version>
1866+
<type>String</type>
1867+
</field>
1868+
</fields>
1869+
</class>
18391870
<class>
18401871
<name>Scm</name>
18411872
<version>4.0.0+</version>

compat/maven-model-builder/src/main/java/org/apache/maven/model/building/FileToRawModelMerger.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ protected void mergeModel_Profiles(
138138
.collect(Collectors.toList()));
139139
}
140140

141+
@Override
142+
protected void mergeModel_Mixins(
143+
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
144+
// don't merge
145+
}
146+
141147
@Override
142148
protected void mergeModelBase_Dependencies(
143149
ModelBase.Builder builder,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
-->
21+
22+
<!-- START SNIPPET: superpom -->
23+
<project>
24+
<modelVersion>4.0.0</modelVersion>
25+
26+
<properties>
27+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
28+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
29+
<!-- Fixed date for reproducible build -->
30+
<project.build.outputTimestamp>1980-02-01T00:00:00Z</project.build.outputTimestamp>
31+
</properties>
32+
33+
<build>
34+
<directory>${project.basedir}/target</directory>
35+
<outputDirectory>${project.build.directory}/classes</outputDirectory>
36+
<finalName>${project.artifactId}-${project.version}</finalName>
37+
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
38+
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
39+
<scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
40+
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
41+
<resources>
42+
<resource>
43+
<directory>${project.basedir}/src/main/resources</directory>
44+
</resource>
45+
<resource>
46+
<directory>${project.basedir}/src/main/resources-filtered</directory>
47+
<filtering>true</filtering>
48+
</resource>
49+
</resources>
50+
<testResources>
51+
<testResource>
52+
<directory>${project.basedir}/src/test/resources</directory>
53+
</testResource>
54+
<testResource>
55+
<directory>${project.basedir}/src/test/resources-filtered</directory>
56+
<filtering>true</filtering>
57+
</testResource>
58+
</testResources>
59+
</build>
60+
61+
<reporting>
62+
<outputDirectory>${project.build.directory}/site</outputDirectory>
63+
</reporting>
64+
65+
</project>
66+
<!-- END SNIPPET: superpom -->

impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ static Model transformNonPom(Model model, MavenProject project) {
209209
.preserveModelVersion(false)
210210
.root(false)
211211
.parent(null)
212+
.mixins(null)
212213
.build(null),
213214
model)
214215
.mailingLists(null)

impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,16 @@ record ModelResolverRequest(
8585
@Nonnull String groupId,
8686
@Nonnull String artifactId,
8787
@Nonnull String version,
88-
@Nullable String classifier)
88+
@Nullable String classifier,
89+
@Nullable String extension)
8990
implements Request<Session> {
91+
public ModelResolverRequest {
92+
Objects.requireNonNull(session, "session cannot be null");
93+
Objects.requireNonNull(groupId, "groupId cannot be null");
94+
Objects.requireNonNull(artifactId, "artifactId cannot be null");
95+
Objects.requireNonNull(version, "version cannot be null");
96+
}
97+
9098
@Nonnull
9199
@Override
92100
public Session getSession() {
@@ -106,12 +114,13 @@ public boolean equals(Object o) {
106114
&& Objects.equals(groupId, that.groupId)
107115
&& Objects.equals(artifactId, that.artifactId)
108116
&& Objects.equals(version, that.version)
109-
&& Objects.equals(classifier, that.classifier);
117+
&& Objects.equals(classifier, that.classifier)
118+
&& Objects.equals(extension, that.extension);
110119
}
111120

112121
@Override
113122
public int hashCode() {
114-
return Objects.hash(repositories, groupId, artifactId, version, classifier);
123+
return Objects.hash(repositories, groupId, artifactId, version, classifier, extension);
115124
}
116125

117126
@Override
@@ -123,6 +132,7 @@ public String toString() {
123132
+ ", artifactId=" + artifactId
124133
+ ", version=" + version
125134
+ ", classifier=" + classifier
135+
+ ", extension=" + extension
126136
+ ']';
127137
}
128138
}

impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInheritanceAssembler.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,16 @@ private void concatPath(StringBuilder url, String path) {
194194
}
195195
}
196196

197+
@Override
198+
protected void mergeModel_Mixins(
199+
Model.Builder builder,
200+
Model target,
201+
Model source,
202+
boolean sourceDominant,
203+
Map<Object, Object> context) {
204+
// do not merge
205+
}
206+
197207
@Override
198208
protected void mergeModelBase_Properties(
199209
ModelBase.Builder builder,

0 commit comments

Comments
 (0)