Browse Source

SONAR-7037 Improve logging on authentication failure

tags/5.3-RC1
Duarte Meneses 8 years ago
parent
commit
fa7c920342

+ 4
- 3
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java View File

@@ -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) {

+ 39
- 7
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java View File

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


+ 6
- 1
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java View File

@@ -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);


+ 116
- 0
sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/ExceptionHandlingMediumTest.java View File

@@ -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");
}
}
}
}

+ 19
- 3
sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java View File

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

Loading…
Cancel
Save