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/09/10 16:05:54 UTC

(maven) branch master updated: [MNG-8678] Address review: improve isFatal Javadoc and add soft-failure test (#13091)

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 348258a120 [MNG-8678] Address review: improve isFatal Javadoc and add soft-failure test (#13091)
348258a120 is described below

commit 348258a120fc2ab405c70cf2a1dab559dc2e1f95
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Sep 10 18:05:47 2026 +0200

    [MNG-8678] Address review: improve isFatal Javadoc and add soft-failure test (#13091)
    
    Follow-up to #13055. The isFatal Javadoc @return tag now explains why
    RuntimeException is treated as fatal on par with Error (both indicate an
    unexpected JVM or framework state). A new test pins the soft-failure path
    so a future change to isFatal cannot silently break --fail-at-end for
    ordinary plugin failures.
---
 .../internal/concurrent/BuildPlanExecutor.java     |  4 +++-
 .../internal/concurrent/BuildPlanExecutorTest.java | 28 ++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
index 85c05e97be..f2c5d68254 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
@@ -599,7 +599,9 @@ private void executeStep(BuildStep step) throws IOException, LifecycleExecutionE
          * them can only be seen by looking at the failures themselves.
          *
          * @param failures The failures collected for a single project
-         * @return {@code true} if the build must be halted
+         * @return {@code true} if the build must be halted; checked exceptions (ordinary plugin failures) are
+         *         soft and allow the reactor to continue with other projects, while {@link RuntimeException}s
+         *         and {@link Error}s indicate an unexpected JVM or framework state and halt the build
          */
         private static boolean isFatal(List<Throwable> failures) {
             return failures.stream().anyMatch(t -> t instanceof RuntimeException || !(t instanceof Exception));
diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutorTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutorTest.java
index f34e8ddcb8..9ef77e95bf 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutorTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutorTest.java
@@ -93,6 +93,34 @@ void exceptionThrownByBuildStepIsRecordedAsBuildFailure() throws Exception {
         assertTrue(session.getResult().getBuildSummary(project) instanceof org.apache.maven.execution.BuildFailure);
     }
 
+    /**
+     * A checked exception thrown by a build step is a soft failure: the reactor must not be halted, and
+     * other projects must still be built when {@code REACTOR_FAIL_AT_END} is in effect. This pins the
+     * soft-failure path of {@code isFatal} so a future change does not silently break {@code --fail-at-end}
+     * for ordinary plugin failures.
+     */
+    @Test
+    void checkedExceptionThrownByBuildStepDoesNotHaltReactor() throws Exception {
+        MavenProject project = newProject();
+        MavenSession session = newSession(project);
+        session.getRequest().setReactorFailureBehavior(MavenExecutionRequest.REACTOR_FAIL_AT_END);
+
+        ReactorContext reactorContext = execute(session, project, event -> {}, plan -> {
+            BuildStep step = plan.step(project, "validate")
+                    .orElseThrow(() -> new IllegalStateException("no validate step in the plan"));
+            // LifecycleExecutionException is a checked exception — the soft-failure case for isFatal.
+            step.exception = new LifecycleExecutionException("plugin failure");
+            step.status.set(BuildStep.FAILED);
+        });
+
+        assertTrue(
+                session.getResult().getBuildSummary(project) instanceof org.apache.maven.execution.BuildFailure,
+                "the project must be recorded as a failure");
+        assertTrue(
+                !reactorContext.getReactorBuildStatus().isHalted(),
+                "a checked exception must not halt the reactor when REACTOR_FAIL_AT_END is set");
+    }
+
     /**
      * A project can end up with more than one failure: when a build step fails, the matching after:* step is
      * still run for cleanup and may fail on its own. Those failures are reported through a wrapper exception,