This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new b8a0ae05a8 [maven-4.0.x] Backport bug fixes from master to maven-4.0.x
(#12091)
b8a0ae05a8 is described below
commit b8a0ae05a834ca2de1fb1985884304baa83cfaaa
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue May 19 15:36:05 2026 +0200
[maven-4.0.x] Backport bug fixes from master to maven-4.0.x (#12091)
* Add Maven version to error message when rejecting model versions (#10921)
Port https://github.com/apache/maven/pull/10899 to master, using
Session.getMavenVersion() instead of reading properties file.
- Modified validateModelVersion() to accept Session parameter and include
Maven version in error messages
- Updated error messages to be single-line and consistent with Maven's style
- Enhanced error messages show which Maven version is rejecting the model
version
- Added test coverage to verify Maven version is included in error messages
This helps users (especially IDE users like NetBeans) understand version
compatibility issues more clearly.
* Improve ProjectBuildingException error messages with detailed problem
reporting (#10975)
* Refactor ProjectBuildingException constructors for improved clarity
* Add unit tests for ProjectBuildingException message generation
* Refactor ProjectBuildingException for improved clarity and maintainability
* Add unit tests for ProjectBuildingException to enhance message validation
* style: Apply Spotless formatting
* Refactor createMessage method to improve error counting logic in
ProjectBuildingException
* Refactor ProjectBuildingException for improved clarity and maintainability
* Refactor createMessage method to streamline error message
formattingRefactor createMessage method to streamline error message formatting
* Refactor:Spotless formating
* Improve dependency scope validation error messages for import scope
(#10991)
- Enhance error message when 'import' scope is used incorrectly in regular
dependencies
- Provide clear guidance that 'import' scope is only valid in
<dependencyManagement> sections
- Replace generic error message with context-aware validation
- Update both Maven 3 (compat) and Maven 4 (impl) implementations for
consistency
- Update tests to verify the improved error messages
- Fix grammar in comments (don't -> not)
- Apply spotless formatting
Before: 'dependencies.dependency.scope' must be one of [provided, compile,
runtime, test, system] but is 'import'.
After: 'dependencies.dependency.scope' has scope 'import'. The 'import'
scope is only valid in <dependencyManagement> sections.
This addresses the confusion reported in
https://github.com/faktorips/faktorips.base/issues/70
where users receive misleading error messages that suggest 'import' scope
is never valid,
when it's actually valid in dependency management sections with type=pom.
* Fixes #10950: Throw ProjectBuildingException for reactor cycles; keep
CycleDetectedException as cause. (#11091)
Non-cycle model problems still use ProjectBuildingException(results).
Remove misleading comment about projectId/pomFile.
* Fix NullPointerException when clearing project properties
Fix #11552
---------
Co-authored-by: Pasan Kavinda Abeysekara
<[email protected]>
Co-authored-by: Arturo Bernal <[email protected]>
Co-authored-by: copilot-swe-agent[bot]
<[email protected]>
---
.../model/validation/DefaultModelValidator.java | 73 +++++++--
.../validation/DefaultModelValidatorTest.java | 4 +
.../java/org/apache/maven/model/ModelTest.java | 13 ++
.../maven/project/DefaultProjectBuilder.java | 9 +-
.../maven/project/ProjectBuildingException.java | 84 ++++++++++-
.../project/ProjectBuildingExceptionTest.java | 163 +++++++++++++++++++++
.../maven/impl/model/DefaultModelValidator.java | 97 ++++++++++--
.../impl/model/DefaultModelValidatorTest.java | 25 ++++
src/mdo/java/WrapperProperties.java | 6 +
src/mdo/model-v3.vm | 6 +-
10 files changed, 444 insertions(+), 36 deletions(-)
diff --git
a/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
b/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
index 5e2d47329d..767054b9bd 100644
---
a/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
+++
b/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
@@ -785,10 +785,10 @@ private void validateEffectiveDependencies(
prefix, "version", problems, errOn30, Version.V20,
d.getVersion(), d.getManagementKey(), d);
/*
- * TODO Extensions like Flex Mojos use custom scopes like
"merged", "internal", "external", etc. In
- * order to don't break backward-compat with those, only
warn but don't error out.
+ * Extensions like Flex Mojos use custom scopes like
"merged", "internal", "external", etc. In
+ * order to not break backward-compat with those, only
warn but don't error out.
*/
- validateEnum(
+ validateDependencyScope(
prefix,
"scope",
problems,
@@ -797,15 +797,11 @@ private void validateEffectiveDependencies(
d.getScope(),
d.getManagementKey(),
d,
- "provided",
- "compile",
- "runtime",
- "test",
- "system");
+ false);
validateEffectiveModelAgainstDependency(prefix, problems,
m, d, request);
} else {
- validateEnum(
+ validateDependencyScope(
prefix,
"scope",
problems,
@@ -814,12 +810,7 @@ private void validateEffectiveDependencies(
d.getScope(),
d.getManagementKey(),
d,
- "provided",
- "compile",
- "runtime",
- "test",
- "system",
- "import");
+ true);
}
}
}
@@ -1462,6 +1453,58 @@ private boolean validateEnum(
return false;
}
+ @SuppressWarnings("checkstyle:parameternumber")
+ private boolean validateDependencyScope(
+ String prefix,
+ String fieldName,
+ ModelProblemCollector problems,
+ Severity severity,
+ Version version,
+ String scope,
+ String sourceHint,
+ InputLocationTracker tracker,
+ boolean isDependencyManagement) {
+ if (scope == null || scope.length() <= 0) {
+ return true;
+ }
+
+ String[] validScopes;
+ if (isDependencyManagement) {
+ validScopes = new String[] {"provided", "compile", "runtime",
"test", "system", "import"};
+ } else {
+ validScopes = new String[] {"provided", "compile", "runtime",
"test", "system"};
+ }
+
+ List<String> values = Arrays.asList(validScopes);
+
+ if (values.contains(scope)) {
+ return true;
+ }
+
+ // Provide a more helpful error message for the 'import' scope
+ if ("import".equals(scope) && !isDependencyManagement) {
+ addViolation(
+ problems,
+ severity,
+ version,
+ prefix + fieldName,
+ sourceHint,
+ "has scope 'import'. The 'import' scope is only valid in
<dependencyManagement> sections.",
+ tracker);
+ } else {
+ addViolation(
+ problems,
+ severity,
+ version,
+ prefix + fieldName,
+ sourceHint,
+ "must be one of " + values + " but is '" + scope + "'.",
+ tracker);
+ }
+
+ return false;
+ }
+
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateModelVersion(
ModelProblemCollector problems, String string,
InputLocationTracker tracker, String... validVersions) {
diff --git
a/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
b/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
index cdfa9caac1..d856b6ae94 100644
---
a/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
+++
b/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
@@ -355,6 +355,10 @@ void testBadDependencyScope() throws Exception {
assertViolations(result, 0, 0, 2);
assertTrue(result.getWarnings().get(0).contains("test:f"));
+ // Check that the import scope error message is more helpful
+ assertTrue(result.getWarnings()
+ .get(0)
+ .contains("has scope 'import'. The 'import' scope is only
valid in <dependencyManagement> sections"));
assertTrue(result.getWarnings().get(1).contains("test:g"));
}
diff --git
a/compat/maven-model/src/test/java/org/apache/maven/model/ModelTest.java
b/compat/maven-model/src/test/java/org/apache/maven/model/ModelTest.java
index 2f872a7bf5..967af237c5 100644
--- a/compat/maven-model/src/test/java/org/apache/maven/model/ModelTest.java
+++ b/compat/maven-model/src/test/java/org/apache/maven/model/ModelTest.java
@@ -66,4 +66,17 @@ void testEqualsIdentity() {
void testToStringNullSafe() {
assertNotNull(new Model().toString());
}
+
+ @Test
+ void testPropertiesClear() {
+ // Test for issue #11552: NullPointerException when clearing properties
+ Model model = new Model();
+ model.addProperty("key1", "value1");
+ model.addProperty("key2", "value2");
+ assertEquals(2, model.getProperties().size());
+
+ // This should not throw NullPointerException
+ model.getProperties().clear();
+ assertEquals(0, model.getProperties().size());
+ }
}
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index f9c645be33..c3e4a73794 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -46,7 +46,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.apache.maven.ProjectCycleException;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.api.ArtifactCoordinates;
import org.apache.maven.api.Language;
@@ -504,10 +503,14 @@ List<ProjectBuildingResult> build(List<File> pomFiles,
boolean recursive) throws
.findAny()
.orElse(null);
if (cycle != null) {
- throw new RuntimeException(new ProjectCycleException(
+ final CycleDetectedException cde =
(CycleDetectedException) cycle.getException();
+ throw new ProjectBuildingException(
+ null,
"The projects in the reactor contain a cyclic
reference: " + cycle.getMessage(),
- (CycleDetectedException) cycle.getException()));
+ null,
+ cde);
}
+
throw new ProjectBuildingException(results);
}
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java
b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java
index d8b3ca5ae8..3aaee22c50 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java
@@ -21,6 +21,8 @@
import java.io.File;
import java.util.List;
+import org.apache.maven.model.building.ModelProblem;
+
/**
*/
public class ProjectBuildingException extends Exception {
@@ -59,7 +61,7 @@ protected ProjectBuildingException(String projectId, String
message, File pomFil
}
public ProjectBuildingException(List<ProjectBuildingResult> results) {
- super("Some problems were encountered while processing the POMs");
+ super(createMessage(results));
this.projectId = "";
this.results = results;
}
@@ -97,4 +99,84 @@ private static String createMessage(String message, String
projectId, File pomFi
}
return buffer.toString();
}
+
+ private static String createMessage(List<ProjectBuildingResult> results) {
+ if (results == null || results.isEmpty()) {
+ return "Some problems were encountered while processing the POMs";
+ }
+
+ long totalProblems = 0;
+ long errorProblems = 0;
+
+ for (ProjectBuildingResult result : results) {
+ List<ModelProblem> problems = result.getProblems();
+ totalProblems += problems.size();
+
+ for (ModelProblem problem : problems) {
+ if (problem.getSeverity() != ModelProblem.Severity.WARNING) {
+ errorProblems++;
+ }
+ }
+ }
+
+ StringBuilder buffer = new StringBuilder(1024);
+ buffer.append(totalProblems);
+ buffer.append(totalProblems == 1 ? " problem was " : " problems were
");
+ buffer.append("encountered while processing the POMs");
+
+ if (errorProblems > 0) {
+ buffer.append(" (")
+ .append(errorProblems)
+ .append(" ")
+ .append(errorProblems > 1 ? "errors" : "error")
+ .append(")");
+ }
+
+ buffer.append(":\n");
+
+ for (ProjectBuildingResult result : results) {
+ if (!result.getProblems().isEmpty()) {
+ String projectInfo = result.getProjectId();
+ if (projectInfo.trim().isEmpty()) {
+ projectInfo =
+ result.getPomFile() != null ?
result.getPomFile().getName() : "unknown project";
+ }
+
+ buffer.append("\n[").append(projectInfo).append("]\n");
+
+ for (ModelProblem problem : result.getProblems()) {
+ if (errorProblems > 0 && problem.getSeverity() ==
ModelProblem.Severity.WARNING) {
+ continue;
+ }
+
+ buffer.append("
[").append(problem.getSeverity()).append("] ");
+ buffer.append(problem.getMessage());
+
+ String location = "";
+ if (!problem.getSource().trim().isEmpty()) {
+ location = problem.getSource();
+ }
+ if (problem.getLineNumber() > 0) {
+ if (!location.isEmpty()) {
+ location += ", ";
+ }
+ location += "line " + problem.getLineNumber();
+ }
+ if (problem.getColumnNumber() > 0) {
+ if (!location.isEmpty()) {
+ location += ", ";
+ }
+ location += "column " + problem.getColumnNumber();
+ }
+
+ if (!location.isEmpty()) {
+ buffer.append(" @ ").append(location);
+ }
+ buffer.append("\n");
+ }
+ }
+ }
+
+ return buffer.toString();
+ }
}
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java
b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java
new file mode 100644
index 0000000000..fae4355e0e
--- /dev/null
+++
b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.project;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.maven.model.building.DefaultModelProblem;
+import org.apache.maven.model.building.ModelProblem;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test for {@link ProjectBuildingException} message generation.
+ */
+@SuppressWarnings("deprecation")
+class ProjectBuildingExceptionTest {
+
+ @Test
+ void testDetailedExceptionMessageWithMultipleProblems() {
+ List<ProjectBuildingResult> results = new ArrayList<>();
+
+ List<ModelProblem> problems1 = new ArrayList<>();
+ Collections.addAll(
+ problems1,
+ new DefaultModelProblem(
+ "Missing required dependency",
+ ModelProblem.Severity.ERROR,
+ null,
+ "pom.xml",
+ 25,
+ 10,
+ null,
+ null),
+ new DefaultModelProblem(
+ "Invalid version format", ModelProblem.Severity.ERROR,
null, "pom.xml", 30, 5, null, null));
+ DefaultProjectBuildingResult result1 =
+ new DefaultProjectBuildingResult("com.example:project1:1.0",
new File("project1/pom.xml"), problems1);
+ results.add(result1);
+
+ List<ModelProblem> problems2 = new ArrayList<>();
+ Collections.addAll(
+ problems2,
+ new DefaultModelProblem(
+ "Deprecated plugin usage",
ModelProblem.Severity.WARNING, null, "pom.xml", 15, 3, null, null));
+ DefaultProjectBuildingResult result2 =
+ new DefaultProjectBuildingResult("com.example:project2:1.0",
new File("project2/pom.xml"), problems2);
+ results.add(result2);
+
+ ProjectBuildingException exception = new
ProjectBuildingException(results);
+ String message = exception.getMessage();
+
+ assertTrue(
+ message.contains("3 problems were encountered while processing
the POMs (2 errors)"),
+ "Message should contain problem count and error count");
+
+ assertTrue(message.contains("[com.example:project1:1.0]"), "Message
should contain project1 identifier");
+
+ assertTrue(message.contains("[com.example:project2:1.0]"), "Message
should contain project2 identifier");
+
+ assertTrue(
+ message.contains("[ERROR] Missing required dependency @
pom.xml, line 25, column 10"),
+ "Message should contain error details with location");
+
+ assertTrue(
+ message.contains("[ERROR] Invalid version format @ pom.xml,
line 30, column 5"),
+ "Message should contain second error details");
+
+ assertTrue(
+ !message.contains("[WARNING]") || message.contains("[WARNING]
Deprecated plugin usage"),
+ "Warnings should be filtered when errors are present or shown
if explicitly included");
+ }
+
+ @Test
+ void testExceptionMessageWithOnlyWarnings() {
+ List<ProjectBuildingResult> results = new ArrayList<>();
+
+ List<ModelProblem> problems = new ArrayList<>();
+ Collections.addAll(
+ problems,
+ new DefaultModelProblem(
+ "Deprecated feature used",
ModelProblem.Severity.WARNING, null, "pom.xml", 10, 1, null, null));
+ DefaultProjectBuildingResult result =
+ new DefaultProjectBuildingResult("com.example:project:1.0",
new File("project/pom.xml"), problems);
+ results.add(result);
+
+ ProjectBuildingException exception = new
ProjectBuildingException(results);
+ String message = exception.getMessage();
+
+ assertTrue(
+ message.contains("1 problem was encountered while processing
the POMs"),
+ "Message should use singular form for single problem");
+
+ assertTrue(
+ message.contains("[WARNING] Deprecated feature used"),
+ "Message should contain warning when no errors are present");
+
+ assertTrue(
+ !message.contains("(") || !message.contains("error"),
+ "Message should not contain error count when there are no
errors");
+ }
+
+ @Test
+ void testExceptionMessageWithEmptyResults() {
+ List<ProjectBuildingResult> results = Collections.emptyList();
+
+ ProjectBuildingException exception = new
ProjectBuildingException(results);
+ String message = exception.getMessage();
+
+ assertEquals(
+ "Some problems were encountered while processing the POMs",
+ message,
+ "Empty results should fall back to generic message");
+ }
+
+ @Test
+ void testExceptionMessageWithNullResults() {
+ ProjectBuildingException exception = new
ProjectBuildingException((List<ProjectBuildingResult>) null);
+ String message = exception.getMessage();
+
+ assertEquals(
+ "Some problems were encountered while processing the POMs",
+ message,
+ "Null results should fall back to generic message");
+ }
+
+ @Test
+ void testExceptionMessageWithUnknownProject() {
+ List<ProjectBuildingResult> results = new ArrayList<>();
+
+ List<ModelProblem> problems = new ArrayList<>();
+ Collections.addAll(
+ problems,
+ new DefaultModelProblem("Some error",
ModelProblem.Severity.ERROR, null, "unknown", 1, 1, null, null));
+ DefaultProjectBuildingResult result = new
DefaultProjectBuildingResult(null, null, problems);
+ results.add(result);
+
+ ProjectBuildingException exception = new
ProjectBuildingException(results);
+ String message = exception.getMessage();
+
+ assertTrue(message.contains("[unknown project]"), "Message should
handle unknown project gracefully");
+ }
+}
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
index 37db33b4e4..44b3353336 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
@@ -372,7 +372,7 @@ public void validateFileModel(Session s, Model m, int
validationLevel, ModelProb
} else if (validationLevel >=
ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) {
validateStringNotEmpty("modelVersion", problems, Severity.ERROR,
Version.V20, m.getModelVersion(), m);
- validateModelVersion(problems, m.getModelVersion(), m,
ModelBuilder.KNOWN_MODEL_VERSIONS);
+ validateModelVersion(s, problems, m.getModelVersion(), m,
ModelBuilder.KNOWN_MODEL_VERSIONS);
Set<String> modules = new HashSet<>();
for (int i = 0, n = m.getModules().size(); i < n; i++) {
@@ -1238,12 +1238,12 @@ private void validateEffectiveDependencies(
d);
/*
- * TODO Extensions like Flex Mojos use custom scopes like
"merged", "internal", "external", etc. In
- * order to don't break backward-compat with those, only
warn but don't error out.
+ * Extensions like Flex Mojos use custom scopes like
"merged", "internal", "external", etc. In
+ * order to not break backward-compat with those, only
warn but don't error out.
*/
ScopeManager scopeManager =
InternalSession.from(s).getSession().getScopeManager();
- validateEnum(
+ validateDependencyScope(
prefix,
"scope",
problems,
@@ -1255,7 +1255,8 @@ private void validateEffectiveDependencies(
scopeManager.getDependencyScopeUniverse().stream()
.map(DependencyScope::getId)
.distinct()
- .toArray(String[]::new));
+ .toArray(String[]::new),
+ false);
validateEffectiveModelAgainstDependency(prefix, problems,
m, d);
} else {
@@ -1265,7 +1266,7 @@ private void validateEffectiveDependencies(
.map(DependencyScope::getId)
.collect(Collectors.toCollection(HashSet::new));
scopes.add("import");
- validateEnum(
+ validateDependencyScope(
prefix,
"scope",
problems,
@@ -1274,7 +1275,8 @@ private void validateEffectiveDependencies(
d.getScope(),
SourceHint.dependencyManagementKey(d),
d,
- scopes.toArray(new String[0]));
+ scopes.toArray(new String[0]),
+ true);
}
}
}
@@ -2023,21 +2025,71 @@ private boolean validateEnum(
return false;
}
+ @SuppressWarnings("checkstyle:parameternumber")
+ private boolean validateDependencyScope(
+ String prefix,
+ String fieldName,
+ ModelProblemCollector problems,
+ Severity severity,
+ Version version,
+ String scope,
+ @Nullable SourceHint sourceHint,
+ InputLocationTracker tracker,
+ String[] validScopes,
+ boolean isDependencyManagement) {
+ if (scope == null || scope.isEmpty()) {
+ return true;
+ }
+
+ List<String> values = Arrays.asList(validScopes);
+
+ if (values.contains(scope)) {
+ return true;
+ }
+
+ // Provide a more helpful error message for the 'import' scope
+ if ("import".equals(scope) && !isDependencyManagement) {
+ addViolation(
+ problems,
+ severity,
+ version,
+ prefix + fieldName,
+ sourceHint,
+ "has scope 'import'. The 'import' scope is only valid in
<dependencyManagement> sections.",
+ tracker);
+ } else {
+ addViolation(
+ problems,
+ severity,
+ version,
+ prefix + fieldName,
+ sourceHint,
+ "must be one of " + values + " but is '" + scope + "'.",
+ tracker);
+ }
+
+ return false;
+ }
+
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateModelVersion(
- ModelProblemCollector problems, String string,
InputLocationTracker tracker, List<String> validVersions) {
- if (string == null || string.isEmpty()) {
+ Session session,
+ ModelProblemCollector problems,
+ String requestedModel,
+ InputLocationTracker tracker,
+ List<String> validVersions) {
+ if (requestedModel == null || requestedModel.isEmpty()) {
return true;
}
- if (validVersions.contains(string)) {
+ if (validVersions.contains(requestedModel)) {
return true;
}
boolean newerThanAll = true;
boolean olderThanAll = true;
for (String validValue : validVersions) {
- final int comparison = compareModelVersions(validValue, string);
+ final int comparison = compareModelVersions(validValue,
requestedModel);
newerThanAll = newerThanAll && comparison < 0;
olderThanAll = olderThanAll && comparison > 0;
}
@@ -2049,8 +2101,10 @@ private boolean validateModelVersion(
Version.V20,
"modelVersion",
null,
- "of '" + string + "' is newer than the versions supported
by this version of Maven: "
- + validVersions + ". Building this project
requires a newer version of Maven.",
+ "of '" + requestedModel + "' is newer than the versions
supported by this Maven version ("
+ + getMavenVersionString(session)
+ + "). Supported modelVersions are: " +
validVersions
+ + ". Building this project requires a newer
version of Maven.",
tracker);
} else if (olderThanAll) {
@@ -2061,8 +2115,10 @@ private boolean validateModelVersion(
Version.V20,
"modelVersion",
null,
- "of '" + string + "' is older than the versions supported
by this version of Maven: "
- + validVersions + ". Building this project
requires an older version of Maven.",
+ "of '" + requestedModel + "' is older than the versions
supported by this Maven version ("
+ + getMavenVersionString(session)
+ + "). Supported modelVersions are: " +
validVersions
+ + ". Building this project requires an older
version of Maven.",
tracker);
} else {
@@ -2072,13 +2128,22 @@ private boolean validateModelVersion(
Version.V20,
"modelVersion",
null,
- "must be one of " + validVersions + " but is '" + string +
"'.",
+ "must be one of " + validVersions + " but is '" +
requestedModel + "'.",
tracker);
}
return false;
}
+ private String getMavenVersionString(Session session) {
+ try {
+ return session.getMavenVersion().toString();
+ } catch (Exception e) {
+ // Fallback for test contexts where RuntimeInformation might not
be available
+ return "unknown";
+ }
+ }
+
/**
* Compares two model versions.
*
diff --git
a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
index 6e6f1d7b49..36ba2745a0 100644
---
a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
+++
b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
@@ -23,6 +23,7 @@
import java.util.Collection;
import java.util.List;
+import org.apache.maven.api.Version;
import org.apache.maven.api.model.Model;
import org.apache.maven.api.services.model.ModelValidator;
import org.apache.maven.impl.InternalSession;
@@ -130,6 +131,11 @@ void setUp() throws Exception {
when(repoSession.getScopeManager()).thenReturn(scopeManager);
session = mock(InternalSession.class);
when(session.getSession()).thenReturn(repoSession);
+
+ // Mock Maven version for error message testing
+ Version mavenVersion = mock(Version.class);
+ when(mavenVersion.toString()).thenReturn("4.0.0-test");
+ when(session.getMavenVersion()).thenReturn(mavenVersion);
}
@AfterEach
@@ -170,6 +176,21 @@ void testModelVersionMessage() throws Exception {
assertTrue(result.getErrors().get(0).contains("'modelVersion' must be
one of"));
}
+ @Test
+ void testModelVersionMessageIncludesMavenVersion() throws Exception {
+ SimpleProblemCollector result = validateFile("bad-modelVersion.xml");
+
+ assertViolations(result, 1, 0, 0);
+
+ String errorMessage = result.getFatals().get(0);
+ assertTrue(errorMessage.contains("modelVersion"));
+ // Should include Maven version (either "4.0.0-test" from mock or
"unknown" as fallback)
+ assertTrue(
+ errorMessage.contains("4.0.0-test") ||
errorMessage.contains("unknown"),
+ "Error message should include Maven version: " + errorMessage);
+ assertTrue(errorMessage.contains("newer than the versions supported by
this Maven version"));
+ }
+
@Test
void testMissingArtifactId() throws Exception {
SimpleProblemCollector result = validate("missing-artifactId-pom.xml");
@@ -391,6 +412,10 @@ void testBadDependencyScope() throws Exception {
assertViolations(result, 0, 0, 2);
assertTrue(result.getWarnings().get(0).contains("groupId='test',
artifactId='f'"));
+ // Check that the import scope error message is more helpful
+ assertTrue(result.getWarnings()
+ .get(0)
+ .contains("has scope 'import'. The 'import' scope is only
valid in <dependencyManagement> sections"));
assertTrue(result.getWarnings().get(1).contains("groupId='test',
artifactId='g'"));
}
diff --git a/src/mdo/java/WrapperProperties.java
b/src/mdo/java/WrapperProperties.java
index f285ec0666..2c1f26ebdd 100644
--- a/src/mdo/java/WrapperProperties.java
+++ b/src/mdo/java/WrapperProperties.java
@@ -378,6 +378,12 @@ public synchronized Object remove(Object key) {
return super.remove(key);
}
+ @Override
+ public synchronized void clear() {
+ keyOrder.clear();
+ super.clear();
+ }
+
@Override
public synchronized void forEach(BiConsumer<? super Object, ? super
Object> action) {
entrySet().forEach(e -> action.accept(e.getKey(), e.getValue()));
diff --git a/src/mdo/model-v3.vm b/src/mdo/model-v3.vm
index a31196bf8c..851a74e085 100644
--- a/src/mdo/model-v3.vm
+++ b/src/mdo/model-v3.vm
@@ -204,7 +204,11 @@ public class ${class.name}
}
#elseif( $field.type == "java.util.Properties" )
LinkedHashMap<String, String> map = new LinkedHashMap<>();
- ${field.name}.forEach((key, value) -> map.put(key.toString(),
value.toString()));
+ ${field.name}.forEach((key, value) -> {
+ if (value != null) {
+ map.put(key.toString(), value.toString());
+ }
+ });
if (!Objects.equals(map, getDelegate().get${cap}())) {
update(getDelegate().with${cap}(map));
}