From 9ce85ef4f4469287aae8a1e43e3d6960d41b746c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 6 Jun 2024 12:31:03 +0200 Subject: [PATCH] [MNG-8066] Default exception handler does not handle recursion (#1558) If there is a recursion in throwable causes, Maven will hang forever, instead to return. --- https://issues.apache.org/jira/browse/MNG-8066 --- .../exception/DefaultExceptionHandler.java | 24 ++++++++++++++----- .../DefaultExceptionHandlerTest.java | 16 +++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) 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 b6c4e8cca5..059e1c8e17 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 @@ -25,7 +25,10 @@ import java.io.IOException; import java.net.ConnectException; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Set; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.model.building.ModelProblem; @@ -88,13 +91,13 @@ Plugins: @Named @Singleton public class DefaultExceptionHandler implements ExceptionHandler { - + @Override public ExceptionSummary handleException(Throwable exception) { return handle("", exception); } private ExceptionSummary handle(String message, Throwable exception) { - String reference = getReference(exception); + String reference = getReference(Collections.newSetFromMap(new IdentityHashMap<>()), exception); List children = null; @@ -156,8 +159,11 @@ public class DefaultExceptionHandler implements ExceptionHandler { } } - private String getReference(Throwable exception) { + private String getReference(Set dejaVu, Throwable exception) { String reference = ""; + if (!dejaVu.add(exception)) { + return reference; + } if (exception != null) { if (exception instanceof MojoExecutionException) { @@ -187,14 +193,14 @@ public class DefaultExceptionHandler implements ExceptionHandler { } if (reference == null || reference.isEmpty()) { - reference = getReference(cause); + reference = getReference(dejaVu, cause); } if (reference == null || reference.isEmpty()) { reference = exception.getClass().getSimpleName(); } } else if (exception instanceof LifecycleExecutionException) { - reference = getReference(exception.getCause()); + reference = getReference(dejaVu, exception.getCause()); } else if (isNoteworthyException(exception)) { reference = exception.getClass().getSimpleName(); } @@ -222,7 +228,8 @@ public class DefaultExceptionHandler implements ExceptionHandler { private String getMessage(String message, Throwable exception) { String fullMessage = (message != null) ? message : ""; - // To break out of possible endless loop when getCause returns "this" + // To break out of possible endless loop when getCause returns "this", or dejaVu for n-level recursion (n>1) + Set dejaVu = Collections.newSetFromMap(new IdentityHashMap<>()); for (Throwable t = exception; t != null && t != t.getCause(); t = t.getCause()) { String exceptionMessage = t.getMessage(); @@ -247,6 +254,11 @@ public class DefaultExceptionHandler implements ExceptionHandler { } else if (!fullMessage.contains(exceptionMessage)) { fullMessage = join(fullMessage, exceptionMessage); } + + if (!dejaVu.add(t)) { + fullMessage = join(fullMessage, "[CIRCULAR REFERENCE]"); + break; + } } return fullMessage.trim(); 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 f3f6075533..c9a84014d2 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 @@ -123,4 +123,20 @@ class DefaultExceptionHandlerTest { String expectedReference = "http://cwiki.apache.org/confluence/display/MAVEN/PluginContainerException"; assertEquals(expectedReference, summary.getReference()); } + + @Test + void testHandleExceptionSelfReferencing() { + RuntimeException boom3 = new RuntimeException("BOOM3"); + RuntimeException boom2 = new RuntimeException("BOOM2", boom3); + RuntimeException boom1 = new RuntimeException("BOOM1", boom2); + boom3.initCause(boom1); + + DefaultExceptionHandler handler = new DefaultExceptionHandler(); + ExceptionSummary summary = handler.handleException(boom1); + + assertEquals("BOOM1: BOOM2: BOOM3: [CIRCULAR REFERENCE]", summary.getMessage()); + assertEquals("", summary.getReference()); + assertEquals(0, summary.getChildren().size()); + assertEquals(boom1, summary.getException()); + } }