[ 
https://issues.apache.org/jira/browse/MNG-8066?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Tamas Cservenak updated MNG-8066:
---------------------------------
    Fix Version/s: 4.0.0-beta-4

> Maven hangs on self-referencing exceptions
> ------------------------------------------
>
>                 Key: MNG-8066
>                 URL: https://issues.apache.org/jira/browse/MNG-8066
>             Project: Maven
>          Issue Type: Bug
>          Components: Core
>            Reporter: François Guillot
>            Assignee: Tamas Cservenak
>            Priority: Major
>             Fix For: 4.0.0, 3.9.8, 4.0.0-beta-4
>
>
> If the code executed by Maven throws a self-referencing exception, such as
> {code:java}
> RuntimeException selfReferencingException = new RuntimeException("BOOM self");
> selfReferencingException.initCause(new Exception("BOOM cause", 
> selfReferencingException));
> throw selfReferencingException;
> {code}
> For instance, if this code is added to an `AbstractExecutionListener`, which 
> is added to the running build:
> {code:java}
> import org.apache.maven.execution.AbstractExecutionListener;
> import org.apache.maven.execution.ExecutionListener;
> import org.apache.maven.execution.ExecutionEvent;
> public class FailingExecutionListener extends AbstractExecutionListener {
>   private final ExecutionListener delegate;
>   public FailingExecutionListener(ExecutionListener delegate) {
>     this.delegate = delegate;
>   }
>   @Override
>   public void sessionStarted(ExecutionEvent event) {
>     if (delegate != null) {
>       delegate.sessionStarted(event);
>     }
>     RuntimeException selfReferencingException = new RuntimeException("BOOM 
> self");
>     selfReferencingException.initCause(new Exception("BOOM cause", 
> selfReferencingException));
>     throw selfReferencingException;
>   }
> }
> {code}
> Maven hangs at the end of the build, in `DefaultExceptionHandler`.
> The code in `DefaultExceptionHandler#getMessage` iterates on a given 
> throwable and its causes. It checks if the cause is not the same throwable, 
> but doesn't protect against a 'two-level' recursion like shown above.
> Note that when printing a stacktrace, Java itself protects against this via 
> the use of a
> {code:java}
> Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
> {code}
> and stops the recursion if encountering an already seen throwable.
> A way to fix this would be to replace the offending cause with a replacement 
> with no cause, such as in
> {code:java}
>     private static Throwable patchCircularCause(Throwable current, Throwable 
> parent) {
>         try {
>             Field causeField = Throwable.class.getDeclaredField("cause");
>             causeField.setAccessible(true);
>             Throwable replacement = new Throwable("[CIRCULAR REFERENCE: " + 
> current + "]");
>             replacement.setStackTrace(current.getStackTrace());
>             causeField.set(parent, replacement);
>             return replacement;
>         } catch (NoSuchFieldException | IllegalAccessException e) {
>             // Couldn't replace the cause, let's return the actual exception.
>             return current;
>         }
>     }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to