@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;
}
*/
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;
* 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<>();
@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;
}
}
}
+ /**
+ * 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());
+ }
+ }
+ }
+
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
@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();
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;
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;
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();
}
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();
}
private void assertNoDuplication(int fileRef) {
assertThat(duplicationRepository.getDuplications(fileRef)).isEmpty();
}
+
}
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;
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'");
}
}
}
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";
@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"));