aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java46
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java7
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java116
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java22
5 files changed, 184 insertions, 14 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
index e16bfaf4275..a292451e818 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
@@ -19,6 +19,7 @@
*/
package org.sonar.batch.bootstrap;
+import org.sonar.api.utils.MessageException;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@@ -126,13 +127,13 @@ public class ServerClient {
public RuntimeException handleHttpException(HttpDownloader.HttpException he) {
if (he.getResponseCode() == 401) {
- return new IllegalStateException(String.format(getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD), he);
+ return MessageException.of(String.format(getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD), he);
}
if (he.getResponseCode() == 403) {
// SONAR-4397 Details are in response content
- return new IllegalStateException(tryParseAsJsonError(he.getResponseContent()), he);
+ return MessageException.of(tryParseAsJsonError(he.getResponseContent()), he);
}
- return new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", he.getResponseCode(), he.getUri(), he.getResponseContent()), he);
+ return MessageException.of(String.format("Fail to execute request [code=%s, url=%s]: %s", he.getResponseCode(), he.getUri(), he.getResponseContent()), he);
}
private static String tryParseAsJsonError(String responseContent) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
index 62bde940a11..3bc8ea8c24a 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
@@ -19,6 +19,9 @@
*/
package org.sonar.batch.bootstrapper;
+import org.sonar.api.utils.MessageException;
+
+import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -95,8 +98,12 @@ public final class Batch {
}
configureLogging();
- bootstrapContainer = GlobalContainer.create(bootstrapProperties, components, preferCache);
- bootstrapContainer.startComponents();
+ try {
+ bootstrapContainer = GlobalContainer.create(bootstrapProperties, components, preferCache);
+ bootstrapContainer.startComponents();
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
this.started = true;
return this;
@@ -108,8 +115,11 @@ public final class Batch {
public Batch executeTask(Map<String, String> analysisProperties, Object... components) {
checkStarted();
configureTaskLogging(analysisProperties);
- bootstrapContainer.executeTask(analysisProperties, components);
- configureLogging();
+ try {
+ bootstrapContainer.executeTask(analysisProperties, components);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
return this;
}
@@ -119,8 +129,11 @@ public final class Batch {
public Batch executeTask(Map<String, String> analysisProperties, IssueListener issueListener) {
checkStarted();
configureTaskLogging(analysisProperties);
- bootstrapContainer.executeTask(analysisProperties, components, issueListener);
- configureLogging();
+ try {
+ bootstrapContainer.executeTask(analysisProperties, components, issueListener);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
return this;
}
@@ -130,6 +143,20 @@ public final class Batch {
}
}
+ private RuntimeException handleException(RuntimeException t) {
+ if (loggingConfig.isVerbose()) {
+ return t;
+ }
+
+ for (Throwable y : Throwables.getCausalChain(t)) {
+ if (y instanceof MessageException) {
+ return (MessageException) y;
+ }
+ }
+
+ return t;
+ }
+
/**
* @since 5.2
*/
@@ -148,7 +175,12 @@ public final class Batch {
private void doStop(boolean swallowException) {
checkStarted();
- bootstrapContainer.stopComponents(swallowException);
+ configureLogging();
+ try {
+ bootstrapContainer.stopComponents(swallowException);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
this.started = false;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java
index 66c827c03b3..7efe3947f3f 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java
@@ -49,6 +49,7 @@ public final class LoggingConfiguration {
private Map<String, String> substitutionVariables = Maps.newHashMap();
private LogOutput logOutput = null;
+ private boolean verbose;
public LoggingConfiguration() {
this(null);
@@ -83,11 +84,15 @@ public final class LoggingConfiguration {
public LoggingConfiguration setVerbose(boolean verbose) {
return setRootLevel(verbose ? LEVEL_ROOT_VERBOSE : LEVEL_ROOT_DEFAULT);
}
+
+ public boolean isVerbose() {
+ return verbose;
+ }
public LoggingConfiguration setVerbose(Map<String, String> props, @Nullable Map<String, String> fallback) {
String logLevel = getFallback("sonar.log.level", props, fallback);
String deprecatedProfilingLevel = getFallback("sonar.log.profilingLevel", props, fallback);
- boolean verbose = "true".equals(getFallback("sonar.verbose", props, fallback)) ||
+ verbose = "true".equals(getFallback("sonar.verbose", props, fallback)) ||
"DEBUG".equals(logLevel) || "TRACE".equals(logLevel) ||
"BASIC".equals(deprecatedProfilingLevel) || "FULL".equals(deprecatedProfilingLevel);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java
new file mode 100644
index 00000000000..c25b5f78c2d
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.batch.mediumtest.log;
+
+import java.util.Collections;
+
+import org.hamcrest.Matchers;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.BeforeClass;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.api.utils.MessageException;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonar.batch.protocol.input.GlobalRepositories;
+import org.sonar.batch.repository.GlobalRepositoriesLoader;
+import org.sonar.batch.bootstrapper.Batch;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class ExceptionHandlingMediumTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private Batch batch;
+ private static ErrorGlobalRepositoriesLoader loader;
+
+ @BeforeClass
+ public static void beforeClass() {
+ loader = new ErrorGlobalRepositoriesLoader();
+ }
+
+ public void setUp(boolean verbose) {
+ Batch.Builder builder = Batch.builder()
+ .setEnableLoggingConfiguration(true)
+ .addComponents(
+ loader,
+ new EnvironmentInformation("mediumTest", "1.0"));
+
+ if (verbose) {
+ builder.setBootstrapProperties(Collections.singletonMap("sonar.verbose", "true"));
+ }
+ batch = builder.build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ setUp(false);
+ loader.withCause = false;
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("Error loading repository");
+ thrown.expectCause(Matchers.nullValue(Throwable.class));
+
+ batch.start();
+ }
+
+ @Test
+ public void testWithCause() throws Exception {
+ setUp(false);
+ loader.withCause = true;
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("Error loading repository");
+ thrown.expectCause(new TypeSafeMatcher<Throwable>() {
+ @Override
+ public void describeTo(Description description) {
+ }
+
+ @Override
+ protected boolean matchesSafely(Throwable item) {
+ return item instanceof IllegalStateException && item.getMessage().equals("Code 401");
+ }
+ });
+
+ batch.start();
+ }
+
+ @Test
+ public void testWithVerbose() throws Exception {
+ setUp(true);
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Unable to load component class");
+ batch.start();
+ }
+
+ private static class ErrorGlobalRepositoriesLoader implements GlobalRepositoriesLoader {
+ boolean withCause = false;
+
+ @Override
+ public GlobalRepositories load(MutableBoolean fromCache) {
+ if (withCause) {
+ IllegalStateException cause = new IllegalStateException("Code 401");
+ throw MessageException.of("Error loading repository", cause);
+ } else {
+ throw MessageException.of("Error loading repository");
+ }
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java
index 92ade0e1252..df4a6bd02a0 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java
@@ -29,11 +29,17 @@ import static com.google.common.collect.Lists.newArrayList;
/**
* Runtime exception for "functional" error. It aims to be displayed to end-users, without any technical information
- * like stack traces. It requires sonar-runner 2.4. Previous versions log stack trace.
+ * like stack traces.
* <p/>
- * Note that by design Maven still logs the stack trace when the option -e is set.
+ *
+ * It's handling depends on the versions of the sonar-batch and sonar-runner. sonar-runner 2.4 will only show the
+ * message associated with this exception.
+ * Starting from sonar-batch 5.3, this is handled in the batch side, and the main goal is to hide all wrappers of this
+ * exception. If this exception is created without cause, then only the message associated with this exception is shown;
+ * otherwise, its causes are also shown.
+ * Previous combinations of sonar-batch/sonar-runner log all stack trace.
* <p/>
- * Message should be clear and complete. Keep in mind that context is not added to the exception.
+ * Message should be clear and complete. Keep in mind that context might not be added to the exception.
* Names of processed resource and decorator are for example not automatically added when throwing {@link MessageException}
* from {@link org.sonar.api.batch.Decorator}.
*
@@ -54,6 +60,16 @@ public class MessageException extends RuntimeException {
this.l10nParams = l10nParams == null ? Collections.emptyList() : Collections.unmodifiableCollection(newArrayList(l10nParams));
}
+ private MessageException(String message, Throwable cause) {
+ super(message, cause);
+ l10nKey = null;
+ l10nParams = Collections.emptyList();
+ }
+
+ public static MessageException of(String message, Throwable cause) {
+ return new MessageException(message, cause);
+ }
+
public static MessageException of(String message) {
return new MessageException(message);
}