You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/08/24 11:36:03 UTC
(maven) branch master updated: Fix duplicate model problem reporting, fixes #12691 (#12707)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new f5649cad75 Fix duplicate model problem reporting, fixes #12691 (#12707)
f5649cad75 is described below
commit f5649cad75eba544a170342038a981372f140a85
Author: Mazen Kamal <71...@users.noreply.github.com>
AuthorDate: Mon Aug 24 14:35:57 2026 +0300
Fix duplicate model problem reporting, fixes #12691 (#12707)
Remove the detailed per-project diagnostics from
ProjectBuildingException#createMessage(List<ProjectBuildingResult>) which
duplicated the rendering already performed by DefaultExceptionHandler.
The exception message now returns a short summary with the problem count,
leaving DefaultExceptionHandler as the single source of detailed output.
Adds a regression test in DefaultExceptionHandlerTest to verify each
model problem is rendered exactly once.
Closes #12691
---
.../maven/project/ProjectBuildingException.java | 87 +----------
.../exception/DefaultExceptionHandlerTest.java | 86 +++++++++++
.../project/ProjectBuildingExceptionTest.java | 163 ---------------------
3 files changed, 90 insertions(+), 246 deletions(-)
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 89ca05d43a..88c76b7aea 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,8 +21,6 @@
import java.io.File;
import java.util.List;
-import org.apache.maven.model.building.ModelProblem;
-
/**
* @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead
*/
@@ -63,7 +61,10 @@ protected ProjectBuildingException(String projectId, String message, File pomFil
}
public ProjectBuildingException(List<ProjectBuildingResult> results) {
- super(createMessage(results));
+ super("Encountered %d problem(s) while processing the POMs"
+ .formatted(results.stream()
+ .mapToLong(result -> result.getProblems().size())
+ .sum()));
this.projectId = "";
this.results = results;
}
@@ -101,84 +102,4 @@ 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/exception/DefaultExceptionHandlerTest.java b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
index 88359e2e8f..e4cc6ab06a 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
@@ -18,17 +18,23 @@
*/
package org.apache.maven.exception;
+import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
+import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.maven.model.Plugin;
+import org.apache.maven.model.building.DefaultModelProblem;
+import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.PluginContainerException;
import org.apache.maven.plugin.PluginExecutionException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.project.ProjectBuildingException;
+import org.apache.maven.project.ProjectBuildingResult;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResolutionException;
@@ -38,6 +44,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
*/
@@ -179,4 +187,82 @@ void testHandleExceptionSelfReferencing() {
assertEquals(0, summary.getChildren().size());
assertEquals(boom1, summary.getException());
}
+
+ @Test
+ void testProjectBuildingExceptionNotRenderedTwice() {
+ ModelProblem problem1 = new DefaultModelProblem(
+ "Malformed POM test.xml: unexpected element",
+ ModelProblem.Severity.FATAL,
+ null,
+ "test.xml",
+ 8,
+ 3,
+ null,
+ null);
+
+ ModelProblem problem2 = new DefaultModelProblem(
+ "Malformed POM test.xml: missing artifactId",
+ ModelProblem.Severity.ERROR,
+ null,
+ "test.xml",
+ 12,
+ 5,
+ null,
+ null);
+
+ ProjectBuildingResult result = mock(ProjectBuildingResult.class);
+ when(result.getProjectId()).thenReturn("test:fail-build:0.1-SNAPSHOT");
+ when(result.getPomFile()).thenReturn(new File("test.xml"));
+ when(result.getProblems()).thenReturn(List.of(problem1, problem2));
+
+ ProjectBuildingException exception = new ProjectBuildingException(List.of(result));
+
+ assertEquals(
+ "Encountered 2 problem(s) while processing the POMs",
+ exception.getMessage(),
+ "exception message must report the total number of problems");
+
+ ExceptionSummary summary = new DefaultExceptionHandler().handleException(exception);
+
+ ExceptionSummary projectSummary = summary.getChildren().get(0);
+ ExceptionSummary problemSummary = projectSummary.getChildren().get(0);
+
+ assertTrue(
+ problemSummary.getMessage().contains("Malformed POM test.xml"),
+ "the handler's own tree should still render the problem detail exactly once");
+
+ assertEquals(1, countOccurrences(flatten(summary), "Malformed POM test.xml: unexpected element"));
+ }
+
+ private static String flatten(ExceptionSummary summary) {
+ if (summary == null) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (summary.getMessage() != null) {
+ sb.append(summary.getMessage());
+ }
+
+ for (ExceptionSummary child : summary.getChildren()) {
+ if (!sb.isEmpty()) {
+ sb.append('\n');
+ }
+ sb.append(flatten(child));
+ }
+
+ return sb.toString();
+ }
+
+ private static int countOccurrences(String text, String substring) {
+ int count = 0;
+ int index = 0;
+
+ while ((index = text.indexOf(substring, index)) != -1) {
+ count++;
+ index += substring.length();
+ }
+
+ return count;
+ }
}
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
deleted file mode 100644
index fae4355e0e..0000000000
--- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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");
- }
-}