diff --git a/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java b/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java index 5f3aa54c20..f1d0afd393 100644 --- a/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java +++ b/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java @@ -225,7 +225,8 @@ public class DefaultExceptionHandler implements ExceptionHandler { private String getMessage(String message, Throwable exception) { String fullMessage = (message != null) ? message : ""; - for (Throwable t = exception; t != null; t = t.getCause()) { + // To break out of possible endless loop when getCause returns "this" + for (Throwable t = exception; t != null && t != t.getCause(); t = t.getCause()) { String exceptionMessage = t.getMessage(); if (t instanceof AbstractMojoExecutionException) { diff --git a/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java b/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java index e7e65b79c8..80d70b1ce4 100644 --- a/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java +++ b/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java @@ -20,6 +20,7 @@ package org.apache.maven.exception; import java.io.IOException; import java.net.ConnectException; +import java.util.concurrent.atomic.AtomicReference; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.MojoExecution; @@ -95,4 +96,32 @@ class DefaultExceptionHandlerTest { String expectedReference = "http://cwiki.apache.org/confluence/display/MAVEN/PluginContainerException"; assertEquals(expectedReference, summary.getReference()); } + + @Test + void testHandleExceptionLoopInCause() { + // Some broken exception that does return "this" as getCause + AtomicReference causeRef = new AtomicReference<>(null); + Exception cause2 = new RuntimeException("loop") { + @Override + public synchronized Throwable getCause() { + return causeRef.get(); + } + }; + causeRef.set(cause2); + + Plugin plugin = new Plugin(); + Exception cause = new PluginContainerException(plugin, null, null, cause2); + cause2.initCause(cause); + PluginDescriptor pluginDescriptor = new PluginDescriptor(); + MojoDescriptor mojoDescriptor = new MojoDescriptor(); + mojoDescriptor.setPluginDescriptor(pluginDescriptor); + MojoExecution mojoExecution = new MojoExecution(mojoDescriptor); + Throwable exception = new PluginExecutionException(mojoExecution, null, cause); + + DefaultExceptionHandler handler = new DefaultExceptionHandler(); + ExceptionSummary summary = handler.handleException(exception); + + String expectedReference = "http://cwiki.apache.org/confluence/display/MAVEN/PluginContainerException"; + assertEquals(expectedReference, summary.getReference()); + } } diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java index 2f1befe6ec..0feab3de0f 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java @@ -150,7 +150,9 @@ public final class CLIReportingUtils { if (e != null) { logger.error(e.getMessage()); - for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { + for (Throwable cause = e.getCause(); + cause != null && cause != cause.getCause(); + cause = cause.getCause()) { logger.error("Caused by: {}", cause.getMessage()); } } diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java index ff7c1ccd21..c321b73427 100644 --- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java +++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java @@ -79,7 +79,7 @@ public class MavenSimpleLogger extends SimpleLogger { writeThrowable(se, stream, "Suppressed", prefix + " "); } Throwable cause = t.getCause(); - if (cause != null) { + if (cause != null && t != cause) { writeThrowable(cause, stream, "Caused by", prefix); } }