This is an automated email from the ASF dual-hosted git repository. elharo 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 cb9c4f16e2 Improve ProjectBuildingException error messages with detailed problem reporting (#10975) cb9c4f16e2 is described below commit cb9c4f16e2d409bd75b4f8bc76e1ae9148a5414c Author: Pasan Kavinda Abeysekara <69195287+pasanabeysek...@users.noreply.github.com> AuthorDate: Sun Jul 27 00:45:57 2025 +0530 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 --- .../maven/project/ProjectBuildingException.java | 84 ++++++++++- .../project/ProjectBuildingExceptionTest.java | 163 +++++++++++++++++++++ 2 files changed, 246 insertions(+), 1 deletion(-) 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 39c20e1082..89ca05d43a 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; + /** * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead */ @@ -61,7 +63,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; } @@ -99,4 +101,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"); + } +}