Uploaded image for project: 'Maven'
  1. Maven
  2. MNG-8066

Maven hangs on self-referencing exceptions

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Fixed
    • None
    • 3.9.8, 4.0.0-beta-4, 4.0.0
    • Core
    • None

    Description

      If the code executed by Maven throws a self-referencing exception, such as

      RuntimeException selfReferencingException = new RuntimeException("BOOM self");
      selfReferencingException.initCause(new Exception("BOOM cause", selfReferencingException));
      throw selfReferencingException;
      

      For instance, if this code is added to an `AbstractExecutionListener`, which is added to the running build:

      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;
        }
      
      }
      

      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

      Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
      

      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

          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;
              }
          }
      

      Attachments

        Issue Links

          Activity

            People

              cstamas Tamas Cservenak
              facewindu François Guillot
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: