]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6997 add VisitException and show current Component info in logs 678/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 3 Dec 2015 18:27:58 +0000 (19:27 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 4 Dec 2015 10:49:31 +0000 (11:49 +0100)
server/sonar-server/src/main/java/org/sonar/server/computation/component/DepthTraversalTypeAwareCrawler.java
server/sonar-server/src/main/java/org/sonar/server/computation/component/PathAwareCrawler.java
server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitException.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitorsCrawler.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistProjectLinksStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateLoadingStepTest.java

index 1ede3d04c47765067e9ed6bbf0bca5d376418308..2882a03a93b323cdacbaa83cf2efa9d3a044b845 100644 (file)
@@ -36,6 +36,14 @@ public final class DepthTraversalTypeAwareCrawler implements ComponentCrawler {
 
   @Override
   public void visit(Component component) {
+    try {
+      visitImpl(component);
+    } catch (RuntimeException e) {
+      VisitException.rethrowOrWrap(e, "Visit of Component %s:%s failed", component.getType(), component.getKey());
+    }
+  }
+
+  private void visitImpl(Component component) {
     if (!verifyDepth(component)) {
       return;
     }
index 8a2e33e4d19528d35029b373abc7392498e0d8a3..5c92e471a1996598f563fd1517deadecdcd5f57c 100644 (file)
  */
 package org.sonar.server.computation.component;
 
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.collect.FluentIterable.from;
 import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
 import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
@@ -31,7 +37,6 @@ import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_
  * As for {@link DepthTraversalTypeAwareCrawler}, this crawler supports max depth visit and ordering.
  */
 public final class PathAwareCrawler<T> implements ComponentCrawler {
-
   private final PathAwareVisitor<T> visitor;
   private final DequeBasedPath<T> stack = new DequeBasedPath<>();
 
@@ -41,6 +46,17 @@ public final class PathAwareCrawler<T> implements ComponentCrawler {
 
   @Override
   public void visit(Component component) {
+    try {
+      visitImpl(component);
+    } catch (RuntimeException e) {
+      VisitException.rethrowOrWrap(
+        e,
+        "Visit failed for Component %s:%s%s",
+        component.getType(), component.getKey(), new ComponentPathPrinter<>(stack));
+    }
+  }
+
+  private void visitImpl(Component component) {
     if (!verifyDepth(component)) {
       return;
     }
@@ -123,4 +139,44 @@ public final class PathAwareCrawler<T> implements ComponentCrawler {
     }
   }
 
+  /**
+   * A simple object wrapping the currentPath allowing to compute the string representing the path only if
+   * the VisitException is actually built (ie. method {@link ComponentPathPrinter#toString()} is called
+   * by the internal {@link String#format(String, Object...)} of
+   * {@link VisitException#rethrowOrWrap(RuntimeException, String, Object...)}.
+   */
+  @Immutable
+  private static final class ComponentPathPrinter<T> {
+
+    private static final Joiner PATH_ELEMENTS_JOINER = Joiner.on("->");
+
+    private final DequeBasedPath<T> currentPath;
+
+    private ComponentPathPrinter(DequeBasedPath<T> currentPath) {
+      this.currentPath = currentPath;
+    }
+
+    @Override
+    public String toString() {
+      if (currentPath.isRoot()) {
+        return "";
+      }
+      return " located " + toKeyPath(currentPath);
+    }
+
+    private static <T> String toKeyPath(Iterable<PathAwareVisitor.PathElement<T>> currentPath) {
+      return PATH_ELEMENTS_JOINER.join(from(currentPath).transform(PathElementToComponentAsString.INSTANCE).skip(1));
+    }
+
+    private enum PathElementToComponentAsString implements Function<PathAwareVisitor.PathElement<?>, String> {
+      INSTANCE;
+
+      @Override
+      @Nonnull
+      public String apply(@Nonnull PathAwareVisitor.PathElement<?> input) {
+        return format("%s:%s", input.getComponent().getType(), input.getComponent().getKey());
+      }
+    }
+  }
+
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitException.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/VisitException.java
new file mode 100644 (file)
index 0000000..1cd04fd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.computation.component;
+
+import static java.lang.String.format;
+
+/**
+ * Wrapper {@link RuntimeException} of any {@link RuntimeException} thrown during the visit of the Component tree.
+ *
+ * Use {@link #rethrowOrWrap(RuntimeException, String, Object...)} to avoid having {@link VisitException} as cause of
+ * another {@link VisitException}
+ */
+public class VisitException extends RuntimeException {
+  public VisitException(String message, RuntimeException cause) {
+    super(message, cause);
+  }
+
+  /**
+   * @param e the {@link RuntimeException} to wrap unless it is an instance of {@link VisitException}
+   * @param pattern a {@link String#format(String, Object...)} pattern
+   * @param args optional {@link String#format(String, Object...)} arguments
+   */
+  public static void rethrowOrWrap(RuntimeException e, String pattern, Object... args) {
+    if (e instanceof VisitException) {
+      throw e;
+    } else {
+      throw new VisitException(format(pattern, args), e);
+    }
+  }
+}
index 0b03029128135bfa88cbb3f31a355c4b1abc5b1e..52ded7e54d60cc08ccb2ee7df4f9198d5671aefe 100644 (file)
@@ -59,6 +59,18 @@ public class VisitorsCrawler implements ComponentCrawler {
 
   @Override
   public void visit(final Component component) {
+    try {
+      visitImpl(component);
+    } catch (RuntimeException e) {
+      VisitException.rethrowOrWrap(
+        e,
+        "Visit of Component %s:" +
+            "%s failed",
+        component.getType(), component.getKey());
+    }
+  }
+
+  private void visitImpl(Component component) {
     MatchVisitorMaxDepth visitorMaxDepth = MatchVisitorMaxDepth.forComponent(component);
     List<VisitorWrapper> preOrderVisitorWrappersToExecute = from(preOrderVisitorWrappers).filter(visitorMaxDepth).toList();
     List<VisitorWrapper> postOrderVisitorWrappersToExecute = from(postOrderVisitorWrappers).filter(visitorMaxDepth).toList();
index a57e5807d2463c4869b99275f42ab7065a7365b5..5515924be2e04ea80de2ec2b1795cc905c666c82 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.server.computation.batch.BatchReportReaderRule;
 import org.sonar.server.computation.batch.TreeRootHolderRule;
 import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.VisitException;
 import org.sonar.server.computation.duplication.Duplicate;
 import org.sonar.server.computation.duplication.Duplication;
 import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
@@ -38,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.server.computation.component.Component.Type.FILE;
 import static org.sonar.server.computation.component.Component.Type.PROJECT;
 import static org.sonar.server.computation.component.ReportComponent.builder;
+import static org.sonar.test.ExceptionCauseMatcher.hasType;
 
 public class LoadDuplicationsFromReportStepTest {
   private static final int LINE = 2;
@@ -135,8 +137,8 @@ public class LoadDuplicationsFromReportStepTest {
     int line = 2;
     reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(line), createInProjectDuplicate(666, line + 1)));
 
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Component with ref '666' can't be found");
+    expectedException.expect(VisitException.class);
+    expectedException.expectCause(hasType(IllegalArgumentException.class).andMessage("Component with ref '666' can't be found"));
 
     underTest.execute();
   }
@@ -146,8 +148,8 @@ public class LoadDuplicationsFromReportStepTest {
     int line = 2;
     reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(line), createInProjectDuplicate(FILE_1_REF, line + 1)));
 
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("file and otherFile Components can not be the same");
+    expectedException.expect(VisitException.class);
+    expectedException.expectCause(hasType(IllegalArgumentException.class).andMessage("file and otherFile Components can not be the same"));
 
     underTest.execute();
   }
@@ -196,4 +198,5 @@ public class LoadDuplicationsFromReportStepTest {
   private void assertNoDuplication(int fileRef) {
     assertThat(duplicationRepository.getDuplications(fileRef)).isEmpty();
   }
+
 }
index cc51e596f44584541b4553542e65d8a063c6f9d3..a758a74525e25948bf79189be573f8a52fcb0db4 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.server.computation.batch.BatchReportReaderRule;
 import org.sonar.server.computation.batch.TreeRootHolderRule;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.ReportComponent;
+import org.sonar.server.computation.component.VisitException;
 import org.sonar.test.DbTests;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -207,9 +208,10 @@ public class PersistProjectLinksStepTest extends BaseStepTest {
 
     try {
       step.execute();
-      failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
-    } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("Link of type 'homepage' has already been declared on component 'ABCD'");
+      failBecauseExceptionWasNotThrown(VisitException.class);
+    } catch (VisitException e) {
+      assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
+      assertThat(e.getCause()).hasMessage("Link of type 'homepage' has already been declared on component 'ABCD'");
     }
   }
 }
index b50e8c69f6d997e6e506afba73214f421c3227b9..9924557f2db548173bac7428490d9f631abef65b 100644 (file)
@@ -29,17 +29,20 @@ import org.sonar.server.computation.batch.TreeRootHolderRule;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.ReportComponent;
 import org.sonar.server.computation.component.SettingsRepository;
+import org.sonar.server.computation.component.VisitException;
 import org.sonar.server.computation.qualitygate.Condition;
 import org.sonar.server.computation.qualitygate.MutableQualityGateHolderRule;
 import org.sonar.server.computation.qualitygate.QualityGate;
 import org.sonar.server.computation.qualitygate.QualityGateService;
 
+import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.guava.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
+import static org.sonar.test.ExceptionCauseMatcher.hasType;
 
 public class QualityGateLoadingStepTest {
   private static final String PROJECT_KEY = "project key";
@@ -74,8 +77,10 @@ public class QualityGateLoadingStepTest {
 
   @Test
   public void execute_sets_default_QualityGate_when_property_value_is_not_a_long() {
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage(String.format("Unsupported value (%s) in property sonar.qualitygate", "10 sds"));
+    expectedException.expect(VisitException.class);
+    expectedException.expectCause(
+      hasType(IllegalStateException.class)
+        .andMessage(format("Unsupported value (%s) in property sonar.qualitygate", "10 sds")));
 
     treeRootHolder.setRoot(PROJECT_ALONE);
     when(settingsRepository.getSettings(PROJECT_ALONE)).thenReturn(new Settings().setProperty("sonar.qualitygate", "10 sds"));