Skip to content

Commit

Permalink
ws-client should not throw HttpException by default
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Brandhof committed Dec 2, 2015
1 parent b420125 commit 92b68fd
Show file tree
Hide file tree
Showing 35 changed files with 493 additions and 589 deletions.
Expand Up @@ -93,7 +93,7 @@ public void basic_authentication_based_on_login_and_password() {
// authenticate // authenticate
WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build()); WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build());
WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate")); WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate"));
assertThat(response.getContent()).isEqualTo("{\"valid\":true}"); assertThat(response.content()).isEqualTo("{\"valid\":true}");
} }


@Test @Test
Expand All @@ -107,7 +107,7 @@ public void basic_authentication_based_on_token() {


WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate")); WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate"));


assertThat(response.getContent()).isEqualTo("{\"valid\":true}"); assertThat(response.content()).isEqualTo("{\"valid\":true}");
} }


/** /**
Expand All @@ -126,7 +126,7 @@ public void basic_authentication_does_not_support_utf8_passwords() {
// authenticate // authenticate
WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build()); WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build());
WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate")); WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate"));
assertThat(response.getContent()).isEqualTo("{\"valid\":false}"); assertThat(response.content()).isEqualTo("{\"valid\":false}");
} }


@Test @Test
Expand Down
Expand Up @@ -21,15 +21,15 @@


import org.picocontainer.injectors.ProviderAdapter; import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.AnalysisMode;
import org.sonar.batch.bootstrap.BatchWsClient;
import org.sonar.batch.cache.WSLoader; import org.sonar.batch.cache.WSLoader;
import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.batch.cache.WSLoader.LoadStrategy;
import org.sonar.home.cache.PersistentCache; import org.sonar.home.cache.PersistentCache;
import org.sonarqube.ws.client.WsClient;


public class AnalysisWSLoaderProvider extends ProviderAdapter { public class AnalysisWSLoaderProvider extends ProviderAdapter {
private WSLoader wsLoader; private WSLoader wsLoader;


public WSLoader provide(AnalysisMode mode, PersistentCache cache, WsClient client) { public WSLoader provide(AnalysisMode mode, PersistentCache cache, BatchWsClient client) {
if (wsLoader == null) { if (wsLoader == null) {
// recreate cache directory if needed for this analysis // recreate cache directory if needed for this analysis
cache.reconfigure(); cache.reconfigure();
Expand Down
Expand Up @@ -42,7 +42,6 @@
import org.sonar.core.platform.RemotePluginFile; import org.sonar.core.platform.RemotePluginFile;
import org.sonar.home.cache.FileCache; import org.sonar.home.cache.FileCache;
import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.WsResponse; import org.sonarqube.ws.client.WsResponse;


import static java.lang.String.format; import static java.lang.String.format;
Expand All @@ -59,9 +58,9 @@ public class BatchPluginInstaller implements PluginInstaller {
private final WSLoader wsLoader; private final WSLoader wsLoader;
private final FileCache fileCache; private final FileCache fileCache;
private final BatchPluginPredicate pluginPredicate; private final BatchPluginPredicate pluginPredicate;
private final WsClient wsClient; private final BatchWsClient wsClient;


public BatchPluginInstaller(WSLoader wsLoader, WsClient wsClient, FileCache fileCache, BatchPluginPredicate pluginPredicate) { public BatchPluginInstaller(WSLoader wsLoader, BatchWsClient wsClient, FileCache fileCache, BatchPluginPredicate pluginPredicate) {
this.wsLoader = wsLoader; this.wsLoader = wsLoader;
this.fileCache = fileCache; this.fileCache = fileCache;
this.pluginPredicate = pluginPredicate; this.pluginPredicate = pluginPredicate;
Expand Down Expand Up @@ -151,8 +150,8 @@ public void download(String filename, File toFile) throws IOException {
LOG.info("Download {}", filename); LOG.info("Download {}", filename);
} }


WsResponse response = wsClient.wsConnector().call(new GetRequest(url)); WsResponse response = wsClient.call(new GetRequest(url));
try (InputStream stream = response.getContentStream()) { try (InputStream stream = response.contentStream()) {
FileUtils.copyInputStreamToFile(stream, toFile); FileUtils.copyInputStreamToFile(stream, toFile);
} }
} }
Expand Down
130 changes: 130 additions & 0 deletions sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchWsClient.java
@@ -0,0 +1,130 @@
/*
* 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.bootstrap;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.WsConnector;
import org.sonarqube.ws.client.WsRequest;
import org.sonarqube.ws.client.WsResponse;

import static java.lang.String.format;

public class BatchWsClient {

private static final Logger LOG = Loggers.get(BatchWsClient.class);

private final WsClient target;
private final boolean hasCredentials;
private final String publicBaseUrl;

public BatchWsClient(WsClient target, boolean hasCredentials, @Nullable String publicBaseUrl) {
this.target = target;
this.hasCredentials = hasCredentials;
if (StringUtils.isBlank(publicBaseUrl)) {
this.publicBaseUrl = target.wsConnector().baseUrl();
} else {
this.publicBaseUrl = publicBaseUrl.replaceAll("(/)+$", "") + "/";
}
}

/**
* @throws IllegalStateException if the request could not be executed due to
* a connectivity problem or timeout. Because networks can
* fail during an exchange, it is possible that the remote server
* accepted the request before the failure
* @throws HttpException if the response code is not in range [200..300)
*/
public WsResponse call(WsRequest request) {
Profiler profiler = Profiler.createIfDebug(LOG).start();
WsResponse response = target.wsConnector().call(request);
profiler.stopDebug(format("%s %d %s", request.getMethod(), response.code(), response.requestUrl()));
failIfUnauthorized(response);
return response;
}

public String baseUrl() {
return target.wsConnector().baseUrl();
}

/**
* The public URL is optionally configured on server. If not, then the regular {@link #baseUrl()} is returned.
* URL has a trailing slash.
* See https://jira.sonarsource.com/browse/SONAR-4239
*/
public String publicBaseUrl() {
return publicBaseUrl;
}

@VisibleForTesting
WsConnector wsConnector() {
return target.wsConnector();
}

private void failIfUnauthorized(WsResponse response) {
if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
if (hasCredentials) {
// credentials are not valid
throw MessageException.of(format("Not authorized. Please check the properties %s and %s.",
CoreProperties.LOGIN, CoreProperties.PASSWORD));
}
// not authenticated - see https://jira.sonarsource.com/browse/SONAR-4048
throw MessageException.of(format("Not authorized. Analyzing this project requires to be authenticated. " +
"Please provide the values of the properties %s and %s.", CoreProperties.LOGIN, CoreProperties.PASSWORD));

}
if (response.code() == HttpURLConnection.HTTP_FORBIDDEN) {
// SONAR-4397 Details are in response content
throw MessageException.of(tryParseAsJsonError(response.content()));
}
response.failIfNotSuccessful();
}

private static String tryParseAsJsonError(String responseContent) {
try {
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(responseContent).getAsJsonObject();
JsonArray errors = obj.getAsJsonArray("errors");
List<String> errorMessages = new ArrayList<>();
for (JsonElement e : errors) {
errorMessages.add(e.getAsJsonObject().get("msg").getAsString());
}
return Joiner.on(", ").join(errorMessages);
} catch (Exception e) {
return responseContent;
}
}
}
Expand Up @@ -25,38 +25,37 @@
import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonarqube.ws.client.HttpConnector; import org.sonarqube.ws.client.HttpConnector;
import org.sonarqube.ws.client.HttpWsClient; import org.sonarqube.ws.client.HttpWsClient;
import org.sonarqube.ws.client.WsClient;


import static java.lang.Integer.parseInt; import static java.lang.Integer.parseInt;
import static java.lang.String.valueOf; import static java.lang.String.valueOf;
import static org.apache.commons.lang.StringUtils.defaultIfBlank; import static org.apache.commons.lang.StringUtils.defaultIfBlank;


@BatchSide @BatchSide
public class WsClientProvider extends ProviderAdapter { public class BatchWsClientProvider extends ProviderAdapter {


static final int CONNECT_TIMEOUT_MS = 5_000; static final int CONNECT_TIMEOUT_MS = 5_000;
static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout"; static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout";
static final int DEFAULT_READ_TIMEOUT_SEC = 60; static final int DEFAULT_READ_TIMEOUT_SEC = 60;


private HttpWsClient wsClient; private BatchWsClient wsClient;


public synchronized WsClient provide(final GlobalProperties settings, final EnvironmentInformation env) { public synchronized BatchWsClient provide(final GlobalProperties settings, final EnvironmentInformation env) {
if (wsClient == null) { if (wsClient == null) {
String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE); String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE);
HttpConnector.Builder builder = new HttpConnector.Builder(); HttpConnector.Builder builder = new HttpConnector.Builder();


// TODO proxy // TODO proxy


String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC)); String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC));
String login = defaultIfBlank(settings.property(CoreProperties.LOGIN), null);
builder builder
.readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000) .readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000)
.connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS) .connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS)
.userAgent(env.toString()) .userAgent(env.toString())
.url(url) .url(url)
.credentials(settings.property(CoreProperties.LOGIN), settings.property(CoreProperties.PASSWORD)) .credentials(login, settings.property(CoreProperties.PASSWORD));
.interceptor(new WsClientLoggingInterceptor());


wsClient = new HttpWsClient(builder.build()); wsClient = new BatchWsClient(new HttpWsClient(builder.build()), login != null, settings.property(CoreProperties.SERVER_BASE_URL));
} }
return wsClient; return wsClient;
} }
Expand Down
Expand Up @@ -91,7 +91,7 @@ private void addBootstrapComponents() {


CachesManager.class, CachesManager.class,
GlobalSettings.class, GlobalSettings.class,
new WsClientProvider(), new BatchWsClientProvider(),
DefaultServer.class, DefaultServer.class,
new GlobalTempFolderProvider(), new GlobalTempFolderProvider(),
DefaultHttpDownloader.class, DefaultHttpDownloader.class,
Expand Down

This file was deleted.

Expand Up @@ -20,9 +20,9 @@
package org.sonar.batch.cache; package org.sonar.batch.cache;


import org.picocontainer.injectors.ProviderAdapter; import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.batch.bootstrap.BatchWsClient;
import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.batch.cache.WSLoader.LoadStrategy;
import org.sonar.home.cache.PersistentCache; import org.sonar.home.cache.PersistentCache;
import org.sonarqube.ws.client.WsClient;


public class StrategyWSLoaderProvider extends ProviderAdapter { public class StrategyWSLoaderProvider extends ProviderAdapter {
private final LoadStrategy strategy; private final LoadStrategy strategy;
Expand All @@ -32,7 +32,7 @@ public StrategyWSLoaderProvider(LoadStrategy strategy) {
this.strategy = strategy; this.strategy = strategy;
} }


public WSLoader provide(PersistentCache cache, WsClient client) { public WSLoader provide(PersistentCache cache, BatchWsClient client) {
if (wsLoader == null) { if (wsLoader == null) {
wsLoader = new WSLoader(strategy, cache, client); wsLoader = new WSLoader(strategy, cache, client);
} }
Expand Down

0 comments on commit 92b68fd

Please sign in to comment.