diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-12-02 11:40:43 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-12-02 14:16:49 +0100 |
commit | 92b68fdb6bca9347e153424fb4f79ceae400fc3d (patch) | |
tree | 78c7cd28e588d4f69c37575d7d84d14c0e45ec1f /sonar-batch/src | |
parent | b4201256a7e4d8b153ecf0cb074f6e264df67f17 (diff) | |
download | sonarqube-92b68fdb6bca9347e153424fb4f79ceae400fc3d.tar.gz sonarqube-92b68fdb6bca9347e153424fb4f79ceae400fc3d.zip |
ws-client should not throw HttpException by default
Diffstat (limited to 'sonar-batch/src')
19 files changed, 378 insertions, 363 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/analysis/AnalysisWSLoaderProvider.java b/sonar-batch/src/main/java/org/sonar/batch/analysis/AnalysisWSLoaderProvider.java index 328478d3f87..7ca47ae787a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/analysis/AnalysisWSLoaderProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/analysis/AnalysisWSLoaderProvider.java @@ -21,15 +21,15 @@ package org.sonar.batch.analysis; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.AnalysisMode; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.cache.WSLoader; import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.home.cache.PersistentCache; -import org.sonarqube.ws.client.WsClient; public class AnalysisWSLoaderProvider extends ProviderAdapter { private WSLoader wsLoader; - public WSLoader provide(AnalysisMode mode, PersistentCache cache, WsClient client) { + public WSLoader provide(AnalysisMode mode, PersistentCache cache, BatchWsClient client) { if (wsLoader == null) { // recreate cache directory if needed for this analysis cache.reconfigure(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java index 56b4199e8e6..950d3e9baf3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java @@ -42,7 +42,6 @@ import org.sonar.core.platform.RemotePlugin; import org.sonar.core.platform.RemotePluginFile; import org.sonar.home.cache.FileCache; import org.sonarqube.ws.client.GetRequest; -import org.sonarqube.ws.client.WsClient; import org.sonarqube.ws.client.WsResponse; import static java.lang.String.format; @@ -59,9 +58,9 @@ public class BatchPluginInstaller implements PluginInstaller { private final WSLoader wsLoader; private final FileCache fileCache; 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.fileCache = fileCache; this.pluginPredicate = pluginPredicate; @@ -151,8 +150,8 @@ public class BatchPluginInstaller implements PluginInstaller { LOG.info("Download {}", filename); } - WsResponse response = wsClient.wsConnector().call(new GetRequest(url)); - try (InputStream stream = response.getContentStream()) { + WsResponse response = wsClient.call(new GetRequest(url)); + try (InputStream stream = response.contentStream()) { FileUtils.copyInputStreamToFile(stream, toFile); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchWsClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchWsClient.java new file mode 100644 index 00000000000..16bac82b84f --- /dev/null +++ b/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; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchWsClientProvider.java index 78e50cc2ca2..a8ac402da35 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchWsClientProvider.java @@ -25,22 +25,21 @@ import org.sonar.api.batch.BatchSide; import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonarqube.ws.client.HttpConnector; import org.sonarqube.ws.client.HttpWsClient; -import org.sonarqube.ws.client.WsClient; import static java.lang.Integer.parseInt; import static java.lang.String.valueOf; import static org.apache.commons.lang.StringUtils.defaultIfBlank; @BatchSide -public class WsClientProvider extends ProviderAdapter { +public class BatchWsClientProvider extends ProviderAdapter { static final int CONNECT_TIMEOUT_MS = 5_000; static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout"; 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) { String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE); HttpConnector.Builder builder = new HttpConnector.Builder(); @@ -48,15 +47,15 @@ public class WsClientProvider extends ProviderAdapter { // TODO proxy String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC)); + String login = defaultIfBlank(settings.property(CoreProperties.LOGIN), null); builder .readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000) .connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS) .userAgent(env.toString()) .url(url) - .credentials(settings.property(CoreProperties.LOGIN), settings.property(CoreProperties.PASSWORD)) - .interceptor(new WsClientLoggingInterceptor()); + .credentials(login, settings.property(CoreProperties.PASSWORD)); - wsClient = new HttpWsClient(builder.build()); + wsClient = new BatchWsClient(new HttpWsClient(builder.build()), login != null, settings.property(CoreProperties.SERVER_BASE_URL)); } return wsClient; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java index 40b5cacb9d1..0440645b5c3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java @@ -91,7 +91,7 @@ public class GlobalContainer extends ComponentContainer { CachesManager.class, GlobalSettings.class, - new WsClientProvider(), + new BatchWsClientProvider(), DefaultServer.class, new GlobalTempFolderProvider(), DefaultHttpDownloader.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptor.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptor.java deleted file mode 100644 index f94bf084ee9..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.base.Joiner; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.Response; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.ArrayList; -import java.util.List; -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 static java.lang.String.format; - -public class WsClientLoggingInterceptor implements Interceptor { - - private static final Logger LOG = Loggers.get(WsClientLoggingInterceptor.class); - - @Override - public Response intercept(Chain chain) throws IOException { - Response response = logAndSendRequest(chain); - if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { - if (StringUtils.isBlank(response.request().header("Authorization"))) { - // 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)); - } - // credentials are not valid - throw MessageException.of(format("Not authorized. Please check 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.body().string())); - } - return response; - } - - private Response logAndSendRequest(Chain chain) throws IOException { - Request request = chain.request(); - Response response; - Profiler profiler = Profiler.createIfDebug(LOG).startTrace(format("%s %s", request.method(), request.url())); - response = chain.proceed(request); - profiler.stopDebug(format("%s %d %s", request.method(), response.code(), request.url())); - return response; - } - - 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; - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/StrategyWSLoaderProvider.java b/sonar-batch/src/main/java/org/sonar/batch/cache/StrategyWSLoaderProvider.java index e7c573753df..e6f7d6b00cb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/StrategyWSLoaderProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/StrategyWSLoaderProvider.java @@ -20,9 +20,9 @@ package org.sonar.batch.cache; import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.home.cache.PersistentCache; -import org.sonarqube.ws.client.WsClient; public class StrategyWSLoaderProvider extends ProviderAdapter { private final LoadStrategy strategy; @@ -32,7 +32,7 @@ public class StrategyWSLoaderProvider extends ProviderAdapter { this.strategy = strategy; } - public WSLoader provide(PersistentCache cache, WsClient client) { + public WSLoader provide(PersistentCache cache, BatchWsClient client) { if (wsLoader == null) { wsLoader = new WSLoader(strategy, cache, client); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java b/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java index 69e7c14538f..9864e574f9b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java @@ -26,12 +26,13 @@ import java.nio.charset.StandardCharsets; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; +import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.home.cache.PersistentCache; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.HttpException; -import org.sonarqube.ws.client.WsClient; import static org.sonar.batch.cache.WSLoader.ServerStatus.ACCESSIBLE; import static org.sonar.batch.cache.WSLoader.ServerStatus.NOT_ACCESSIBLE; @@ -50,7 +51,7 @@ public class WSLoader { } private final LoadStrategy defautLoadStrategy; - private final WsClient client; + private final BatchWsClient wsClient; private final PersistentCache cache; private ServerStatus serverStatus; @@ -58,7 +59,7 @@ public class WSLoader { @Override public String load(String id) throws IOException { GetRequest getRequest = new GetRequest(id); - try (Reader reader = client.wsConnector().call(getRequest).getContentReader()) { + try (Reader reader = wsClient.call(getRequest).contentReader()) { String str = IOUtils.toString(reader); try { cache.put(id, str.getBytes(StandardCharsets.UTF_8)); @@ -81,7 +82,7 @@ public class WSLoader { @Override public InputStream load(String id) throws IOException { GetRequest getRequest = new GetRequest(id); - try (InputStream is = client.wsConnector().call(getRequest).getContentStream()) { + try (InputStream is = wsClient.call(getRequest).contentStream()) { try { cache.put(id, is); } catch (IOException e) { @@ -111,11 +112,11 @@ public class WSLoader { } } - public WSLoader(LoadStrategy strategy, PersistentCache cache, WsClient client) { + public WSLoader(LoadStrategy strategy, PersistentCache cache, BatchWsClient wsClient) { this.defautLoadStrategy = strategy; this.serverStatus = UNKNOWN; this.cache = cache; - this.client = client; + this.wsClient = wsClient; } @Nonnull @@ -193,7 +194,7 @@ public class WSLoader { throw new IllegalStateException(FAIL_MSG, serverNotAvailable.getCause()); } } - throw new IllegalStateException("Server is not available: " + client.wsConnector().baseUrl(), serverNotAvailable.getCause()); + throw new IllegalStateException("Server is not available: " + wsClient.baseUrl(), serverNotAvailable.getCause()); } } @@ -201,7 +202,6 @@ public class WSLoader { T load(String id) throws IOException; } - @Nonnull private <T> WSLoaderResult<T> loadFromCache(String id, DataLoader<T> loader) throws NotAvailableException { T result; @@ -217,7 +217,6 @@ public class WSLoader { return new WSLoaderResult<>(result, true); } - @Nonnull private <T> WSLoaderResult<T> loadFromServer(String id, DataLoader<T> loader) throws NotAvailableException { if (isOffline()) { throw new NotAvailableException("Server not available"); @@ -226,11 +225,10 @@ public class WSLoader { T t = loader.load(id); switchToOnline(); return new WSLoaderResult<>(t, false); + } catch (HttpException | MessageException e) { + // fail fast if it could connect but there was a application-level error + throw e; } catch (IllegalStateException e) { - if (e.getCause() instanceof HttpException) { - // fail fast if it could connect but there was a application-level error - throw e; - } switchToOffline(); throw new NotAvailableException(e); } catch (Exception e) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java index 209dd29712f..524a9a034f5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java @@ -20,10 +20,12 @@ package org.sonar.batch.report; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; import com.google.common.io.Files; import com.squareup.okhttp.HttpUrl; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.Writer; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -42,11 +44,13 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.text.JsonWriter; import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.protocol.output.BatchReportWriter; import org.sonar.batch.scan.ImmutableProjectReactor; +import org.sonarqube.ws.MediaTypes; import org.sonarqube.ws.WsCe; -import org.sonarqube.ws.client.WsClient; -import org.sonarqube.ws.client.ce.SubmitWsRequest; +import org.sonarqube.ws.client.PostRequest; +import org.sonarqube.ws.client.WsResponse; import static org.apache.commons.lang.StringUtils.defaultIfBlank; @@ -60,7 +64,7 @@ public class ReportPublisher implements Startable { public static final String METADATA_DUMP_FILENAME = "analysis-details.json"; private final Settings settings; - private final WsClient wsClient; + private final BatchWsClient wsClient; private final AnalysisContextReportPublisher contextPublisher; private final ImmutableProjectReactor projectReactor; private final DefaultAnalysisMode analysisMode; @@ -70,7 +74,7 @@ public class ReportPublisher implements Startable { private File reportDir; private BatchReportWriter writer; - public ReportPublisher(Settings settings, WsClient wsClient, AnalysisContextReportPublisher contextPublisher, + public ReportPublisher(Settings settings, BatchWsClient wsClient, AnalysisContextReportPublisher contextPublisher, ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { this.settings = settings; this.wsClient = wsClient; @@ -145,15 +149,22 @@ public class ReportPublisher implements Startable { LOG.debug("Upload report"); long startTime = System.currentTimeMillis(); ProjectDefinition projectDefinition = projectReactor.getRoot(); - SubmitWsRequest submitRequest = new SubmitWsRequest(); - submitRequest.setProjectKey(projectDefinition.getKey()); - submitRequest.setProjectName(projectDefinition.getName()); - submitRequest.setProjectBranch(projectDefinition.getBranch()); - submitRequest.setReport(report); - WsCe.SubmitResponse submitResponse = wsClient.computeEngine().submit(submitRequest); - long stopTime = System.currentTimeMillis(); - LOG.info("Analysis report uploaded in " + (stopTime - startTime) + "ms"); - return submitResponse.getTaskId(); + PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, report); + PostRequest post = new PostRequest("api/ce/submit") + .setMediaType(MediaTypes.PROTOBUF) + .setParam("projectKey", projectDefinition.getKey()) + .setParam("projectName", projectDefinition.getName()) + .setParam("projectBranch", projectDefinition.getBranch()) + .setPart("report", filePart); + WsResponse response = wsClient.call(post).failIfNotSuccessful(); + try (InputStream protobuf = response.contentStream()) { + return WsCe.SubmitResponse.parser().parseFrom(protobuf).getTaskId(); + } catch (Exception e) { + throw Throwables.propagate(e); + } finally { + long stopTime = System.currentTimeMillis(); + LOG.info("Analysis report uploaded in " + (stopTime - startTime) + "ms"); + } } @VisibleForTesting @@ -165,13 +176,13 @@ public class ReportPublisher implements Startable { String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); metadata.put("projectKey", effectiveKey); - URL dashboardUrl = HttpUrl.parse(publicUrl()).newBuilder() + URL dashboardUrl = HttpUrl.parse(wsClient.publicBaseUrl()).newBuilder() .addPathSegment("dashboard").addPathSegment("index").addPathSegment(effectiveKey) .build() .url(); metadata.put("dashboardUrl", dashboardUrl.toExternalForm()); - URL taskUrl = HttpUrl.parse(publicUrl()).newBuilder() + URL taskUrl = HttpUrl.parse(wsClient.publicBaseUrl()).newBuilder() .addPathSegment("api").addPathSegment("ce").addPathSegment("task") .addQueryParameter("id", taskId) .build() @@ -202,12 +213,4 @@ public class ReportPublisher implements Startable { throw new IllegalStateException("Unable to dump " + file, e); } } - - /** - * The public URL is optionally configured on server. If not, then the regular URL is returned. - * See https://jira.sonarsource.com/browse/SONAR-4239 - */ - private String publicUrl() { - return defaultIfBlank(settings.getString(CoreProperties.SERVER_BASE_URL), wsClient.wsConnector().baseUrl()); - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java index 3a00c304aaa..8168c676b8a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java @@ -36,6 +36,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang.mutable.MutableBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.utils.MessageException; import org.sonar.batch.cache.WSLoader; import org.sonar.batch.cache.WSLoaderResult; import org.sonar.batch.util.BatchUtils; @@ -88,6 +89,9 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad HttpException http = (HttpException) t; return http.code() != HttpURLConnection.HTTP_NOT_FOUND; } + if (t instanceof MessageException) { + return true; + } } return false; diff --git a/sonar-batch/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java index 5687ebdc27c..b175faa6bc6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/analysis/AnalysisWSLoaderProviderTest.java @@ -19,39 +19,27 @@ */ package org.sonar.batch.analysis; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.sonar.api.batch.AnalysisMode; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.cache.WSLoader; import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.home.cache.PersistentCache; -import org.sonarqube.ws.client.WsClient; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; public class AnalysisWSLoaderProviderTest { - @Mock - private PersistentCache cache; - @Mock - private WsClient client; + PersistentCache cache = mock(PersistentCache.class); + BatchWsClient wsClient = mock(BatchWsClient.class); + AnalysisMode mode = mock(AnalysisMode.class); - @Mock - private AnalysisMode mode; - - private AnalysisWSLoaderProvider loaderProvider; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - loaderProvider = new AnalysisWSLoaderProvider(); - } + AnalysisWSLoaderProvider underTest = new AnalysisWSLoaderProvider(); @Test public void testDefault() { - WSLoader loader = loaderProvider.provide(mode, cache, client); + WSLoader loader = underTest.provide(mode, cache, wsClient); assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java index fdc159569a0..0acdb08e87c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java @@ -29,7 +29,6 @@ import org.sonar.batch.cache.WSLoader; import org.sonar.batch.cache.WSLoaderResult; import org.sonar.core.platform.RemotePlugin; import org.sonar.home.cache.FileCache; -import org.sonarqube.ws.client.WsClient; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; @@ -47,7 +46,7 @@ public class BatchPluginInstallerTest { public ExpectedException thrown = ExpectedException.none(); FileCache fileCache = mock(FileCache.class); - WsClient wsClient = mock(WsClient.class); + BatchWsClient wsClient = mock(BatchWsClient.class); BatchPluginPredicate pluginPredicate = mock(BatchPluginPredicate.class); @Test @@ -55,9 +54,9 @@ public class BatchPluginInstallerTest { WSLoader wsLoader = mock(WSLoader.class); when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<>("checkstyle\nsqale", true)); - BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); + BatchPluginInstaller underTest = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); - List<RemotePlugin> remotePlugins = installer.listRemotePlugins(); + List<RemotePlugin> remotePlugins = underTest.listRemotePlugins(); assertThat(remotePlugins).extracting("key").containsOnly("checkstyle", "sqale"); } @@ -67,10 +66,10 @@ public class BatchPluginInstallerTest { when(fileCache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar); WSLoader wsLoader = mock(WSLoader.class); - BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); + BatchPluginInstaller underTest = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); RemotePlugin remote = new RemotePlugin("checkstyle").setFile("checkstyle-plugin.jar", "fakemd5_1"); - File file = installer.download(remote); + File file = underTest.download(remote); assertThat(file).isEqualTo(pluginJar); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java index ac5a9c86683..3b19135fde0 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchWsClientProviderTest.java @@ -24,33 +24,29 @@ import java.util.Map; import org.junit.Test; import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonarqube.ws.client.HttpConnector; -import org.sonarqube.ws.client.WsClient; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.batch.bootstrap.WsClientProvider.CONNECT_TIMEOUT_MS; -import static org.sonar.batch.bootstrap.WsClientProvider.DEFAULT_READ_TIMEOUT_SEC; -public class WsClientProviderTest { +public class BatchWsClientProviderTest { - WsClientProvider underTest = new WsClientProvider(); + BatchWsClientProvider underTest = new BatchWsClientProvider(); EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3"); @Test public void provide_client_with_default_settings() { GlobalProperties settings = new GlobalProperties(new HashMap<String, String>()); - WsClient client = underTest.provide(settings, env); + BatchWsClient client = underTest.provide(settings, env); assertThat(client).isNotNull(); + assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/"); + assertThat(client.publicBaseUrl()).isEqualTo("http://localhost:9000/"); HttpConnector httpConnector = (HttpConnector) client.wsConnector(); assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/"); assertThat(httpConnector.okHttpClient().getProxy()).isNull(); assertThat(httpConnector.okHttpClient().getConnectTimeout()).isEqualTo(5_000); assertThat(httpConnector.okHttpClient().getReadTimeout()).isEqualTo(60_000); assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3"); - assertThat(httpConnector.okHttpClient().interceptors()) - .hasSize(1) - .hasOnlyElementsOfType(WsClientLoggingInterceptor.class); } @Test @@ -62,7 +58,7 @@ public class WsClientProviderTest { props.put("sonar.ws.timeout", "42"); GlobalProperties settings = new GlobalProperties(props); - WsClient client = underTest.provide(settings, env); + BatchWsClient client = underTest.provide(settings, env); assertThat(client).isNotNull(); HttpConnector httpConnector = (HttpConnector) client.wsConnector(); @@ -74,8 +70,8 @@ public class WsClientProviderTest { @Test public void build_singleton() { GlobalProperties settings = new GlobalProperties(new HashMap<String, String>()); - WsClient first = underTest.provide(settings, env); - WsClient second = underTest.provide(settings, env); + BatchWsClient first = underTest.provide(settings, env); + BatchWsClient second = underTest.provide(settings, env); assertThat(first).isSameAs(second); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java new file mode 100644 index 00000000000..f7b9a216b7b --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchWsClientTest.java @@ -0,0 +1,142 @@ +/* + * 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 java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mockito; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.MockWsResponse; +import org.sonarqube.ws.client.WsClient; +import org.sonarqube.ws.client.WsRequest; +import org.sonarqube.ws.client.WsResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BatchWsClientTest { + + @Rule + public LogTester logTester = new LogTester(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS); + + @Test + public void define_public_url() { + when(wsClient.wsConnector().baseUrl()).thenReturn("https://local/"); + BatchWsClient underTest = new BatchWsClient(wsClient, true, "https://public/"); + assertThat(underTest.baseUrl()).isEqualTo("https://local/"); + assertThat(underTest.publicBaseUrl()).isEqualTo("https://public/"); + } + + /** + * Returned URL has trailing slash, even if configured URL doesn't have. + * That's useful for {@link com.squareup.okhttp.HttpUrl} + */ + @Test + public void public_url_has_trailing_slash() { + BatchWsClient underTest = new BatchWsClient(wsClient, true, "https://public"); + assertThat(underTest.publicBaseUrl()).isEqualTo("https://public/"); + } + + + @Test + public void public_url_is_the_base_url_by_default() { + when(wsClient.wsConnector().baseUrl()).thenReturn("https://local/"); + BatchWsClient underTest = new BatchWsClient(wsClient, true, null); + assertThat(underTest.publicBaseUrl()).isEqualTo("https://local/"); + } + + @Test + public void log_and_profile_request_if_debug_level() throws Exception { + WsRequest request = newRequest(); + WsResponse response = newResponse().setRequestUrl("https://local/api/issues/search"); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + logTester.setLevel(LoggerLevel.DEBUG); + BatchWsClient underTest = new BatchWsClient(wsClient, false, null); + + WsResponse result = underTest.call(request); + + // do not fail the execution -> interceptor returns the response + assertThat(result).isSameAs(response); + + // check logs + List<String> debugLogs = logTester.logs(LoggerLevel.DEBUG); + assertThat(debugLogs).hasSize(1); + assertThat(debugLogs.get(0)).contains("GET 200 https://local/api/issues/search | time="); + } + + @Test + public void fail_if_requires_credentials() throws Exception { + expectedException.expect(MessageException.class); + expectedException + .expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password."); + + WsRequest request = newRequest(); + WsResponse response = newResponse().setCode(401); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + new BatchWsClient(wsClient, false, null).call(request); + } + + @Test + public void fail_if_credentials_are_not_valid() throws Exception { + expectedException.expect(MessageException.class); + expectedException.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password."); + + WsRequest request = newRequest(); + WsResponse response = newResponse().setCode(401); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + new BatchWsClient(wsClient, /* credentials are configured */true, null).call(request); + } + + @Test + public void fail_if_requires_permission() throws Exception { + expectedException.expect(MessageException.class); + expectedException.expectMessage("missing scan permission, missing another permission"); + + WsRequest request = newRequest(); + WsResponse response = newResponse() + .setCode(403) + .setContent("{\"errors\":[{\"msg\":\"missing scan permission\"}, {\"msg\":\"missing another permission\"}]}"); + when(wsClient.wsConnector().call(request)).thenReturn(response); + + new BatchWsClient(wsClient, true, null).call(request); + } + + private MockWsResponse newResponse() { + return new MockWsResponse().setRequestUrl("https://local/api/issues/search"); + } + + private WsRequest newRequest() { + return new GetRequest("api/issues/search"); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptorTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptorTest.java deleted file mode 100644 index 4655371e88e..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WsClientLoggingInterceptorTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.squareup.okhttp.Interceptor; -import com.squareup.okhttp.MediaType; -import com.squareup.okhttp.Protocol; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.Response; -import com.squareup.okhttp.ResponseBody; -import java.util.List; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.sonar.api.utils.MessageException; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class WsClientLoggingInterceptorTest { - - @Rule - public LogTester logTester = new LogTester(); - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - WsClientLoggingInterceptor underTest = new WsClientLoggingInterceptor(); - Interceptor.Chain chain = mock(Interceptor.Chain.class); - - @Test - public void log_and_profile_request_if_debug_level() throws Exception { - Request request = newRequest(); - Response response = newResponse(request, 200, ""); - when(chain.request()).thenReturn(request); - when(chain.proceed(request)).thenReturn(response); - - logTester.setLevel(LoggerLevel.DEBUG); - Response result = underTest.intercept(chain); - - // do not fail the execution -> interceptor returns the response - assertThat(result).isSameAs(response); - - // check logs - List<String> debugLogs = logTester.logs(LoggerLevel.DEBUG); - assertThat(debugLogs).hasSize(1); - assertThat(debugLogs.get(0)).contains("GET 200 https://localhost:9000/api/issues/search | time="); - List<String> traceLogs = logTester.logs(LoggerLevel.TRACE); - assertThat(traceLogs).hasSize(1); - assertThat(traceLogs.get(0)).isEqualTo("GET https://localhost:9000/api/issues/search"); - } - - @Test - public void fail_if_requires_authentication() throws Exception { - expectedException.expect(MessageException.class); - expectedException - .expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password."); - - Request request = newRequest(); - Response response = newResponse(request, 401, ""); - when(chain.request()).thenReturn(request); - when(chain.proceed(request)).thenReturn(response); - - underTest.intercept(chain); - } - - @Test - public void fail_if_credentials_are_not_valid() throws Exception { - expectedException.expect(MessageException.class); - expectedException.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password."); - - Request request = new Request.Builder() - .url("https://localhost:9000/api/issues/search") - .header("Authorization", "Basic BAD_CREDENTIALS") - .get() - .build(); - Response response = newResponse(request, 401, ""); - when(chain.request()).thenReturn(request); - when(chain.proceed(request)).thenReturn(response); - - underTest.intercept(chain); - } - - @Test - public void fail_if_requires_permission() throws Exception { - expectedException.expect(MessageException.class); - expectedException.expectMessage("missing scan permission, missing another permission"); - - Request request = newRequest(); - Response response = newResponse(request, 403, "{\"errors\":[{\"msg\":\"missing scan permission\"}, {\"msg\":\"missing another permission\"}]}"); - when(chain.request()).thenReturn(request); - when(chain.proceed(request)).thenReturn(response); - - underTest.intercept(chain); - } - - private Request newRequest() { - return new Request.Builder().url("https://localhost:9000/api/issues/search").get().build(); - } - - private Response newResponse(Request getRequest, int code, String jsonBody) { - return new Response.Builder().request(getRequest) - .code(code) - .protocol(Protocol.HTTP_1_1) - .body(ResponseBody.create(MediaType.parse("application/json"), jsonBody)) - .build(); - } - -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java index 4134b7f6653..0b36dfd3997 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/StrategyWSLoaderProviderTest.java @@ -23,9 +23,9 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.home.cache.PersistentCache; -import org.sonarqube.ws.client.WsClient; import static org.assertj.core.api.Assertions.assertThat; @@ -34,7 +34,7 @@ public class StrategyWSLoaderProviderTest { private PersistentCache cache; @Mock - private WsClient client; + private BatchWsClient client; @Before public void setUp() { diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderTest.java index 2db585445f8..8b553e8eb02 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/cache/WSLoaderTest.java @@ -28,11 +28,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.InOrder; import org.mockito.Mockito; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.cache.WSLoader.LoadStrategy; import org.sonar.home.cache.PersistentCache; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.MockWsResponse; -import org.sonarqube.ws.client.WsClient; import org.sonarqube.ws.client.WsConnector; import org.sonarqube.ws.client.WsRequest; @@ -55,7 +55,7 @@ public class WSLoaderTest { @Rule public ExpectedException exception = ExpectedException.none(); - WsClient ws = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS); + BatchWsClient ws = mock(BatchWsClient.class, Mockito.RETURNS_DEEP_STUBS); PersistentCache cache = mock(PersistentCache.class); @Test @@ -88,7 +88,7 @@ public class WSLoaderTest { public void put_stream_in_cache() throws IOException { InputStream input = IOUtils.toInputStream("is"); - when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(input)); + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(input)); when(cache.getStream(ID)).thenReturn(input); // SERVER_FIRST -> load from server then put to cache @@ -96,25 +96,24 @@ public class WSLoaderTest { WSLoaderResult<InputStream> result = underTest.loadStream(ID); assertThat(result.get()).isEqualTo(input); - WsConnector wsConnector = ws.wsConnector(); - InOrder inOrder = inOrder(wsConnector, cache); - inOrder.verify(wsConnector).call(any(WsRequest.class)); + InOrder inOrder = inOrder(ws, cache); + inOrder.verify(ws).call(any(WsRequest.class)); inOrder.verify(cache).put(eq(ID), any(InputStream.class)); inOrder.verify(cache).getStream(ID); - verifyNoMoreInteractions(cache, wsConnector); + verifyNoMoreInteractions(cache, ws); } @Test public void test_cache_strategy_fallback() throws IOException { turnCacheEmpty(); - when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); assertResult(loader.loadString(ID), serverValue, false); - InOrder inOrder = inOrder(ws.wsConnector(), cache); + InOrder inOrder = inOrder(ws, cache); inOrder.verify(cache).getString(ID); - inOrder.verify(ws.wsConnector()).call(any(WsRequest.class)); + inOrder.verify(ws).call(any(WsRequest.class)); } @Test @@ -125,14 +124,14 @@ public class WSLoaderTest { assertResult(loader.loadString(ID), cacheValue, true); - InOrder inOrder = inOrder(ws.wsConnector(), cache); - inOrder.verify(ws.wsConnector()).call(any(WsRequest.class)); + InOrder inOrder = inOrder(ws, cache); + inOrder.verify(ws).call(any(WsRequest.class)); inOrder.verify(cache).getString(ID); } @Test public void test_put_cache() throws IOException { - when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); loader.loadString(ID); verify(cache).put(ID, serverValue.getBytes()); @@ -171,17 +170,14 @@ public class WSLoaderTest { @Test public void test_throw_http_exceptions() { - HttpException httpException = new HttpException("url", 500, "Internal Error"); - IllegalStateException wrapperException = new IllegalStateException(httpException); - - when(ws.wsConnector().call(any(WsRequest.class))).thenThrow(wrapperException); + when(ws.call(any(WsRequest.class))).thenThrow(new HttpException("url", 500)); WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); try { loader.loadString(ID); fail("IllegalStateException expected"); - } catch (IllegalStateException e) { + } catch (HttpException e) { // cache should not be used verifyNoMoreInteractions(cache); } @@ -223,7 +219,7 @@ public class WSLoaderTest { @Test public void test_server_strategy() throws IOException { - when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); assertResult(loader.loadString(ID), serverValue, false); @@ -241,7 +237,7 @@ public class WSLoaderTest { @Test public void test_string() { - when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); + when(ws.call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); assertResult(loader.loadString(ID), serverValue, false); } @@ -251,7 +247,7 @@ public class WSLoaderTest { } private void assertUsedServer(int times) { - verify(ws.wsConnector(), times(times)).call(any(WsRequest.class)); + verify(ws, times(times)).call(any(WsRequest.class)); } private void assertResult(WSLoaderResult<String> result, String expected, boolean fromCache) { @@ -261,7 +257,7 @@ public class WSLoaderTest { } private void turnServerOffline() { - when(ws.wsConnector().call(any(WsRequest.class))).thenThrow(new IllegalStateException()); + when(ws.call(any(WsRequest.class))).thenThrow(new IllegalStateException()); } private void turnCacheEmpty() throws IOException { diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java index d1c5e1a93c6..dce14a13b8e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java @@ -35,9 +35,9 @@ import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.bootstrap.BatchWsClient; import org.sonar.batch.scan.ImmutableProjectReactor; import org.sonar.test.JsonAssert; -import org.sonarqube.ws.client.WsClient; import static org.apache.commons.io.FileUtils.readFileToString; import static org.assertj.core.api.Assertions.assertThat; @@ -54,7 +54,7 @@ public class ReportPublisherTest { DefaultAnalysisMode mode = mock(DefaultAnalysisMode.class); Settings settings = new Settings(); - WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS); + BatchWsClient wsClient = mock(BatchWsClient.class, Mockito.RETURNS_DEEP_STUBS); ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class); ProjectDefinition root; AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class); @@ -63,7 +63,8 @@ public class ReportPublisherTest { public void setUp() { root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot()); when(reactor.getRoot()).thenReturn(root); - when(wsClient.wsConnector().baseUrl()).thenReturn("https://localhost"); + when(wsClient.baseUrl()).thenReturn("https://localhost/"); + when(wsClient.publicBaseUrl()).thenReturn("https://public/"); } @Test @@ -73,42 +74,21 @@ public class ReportPublisherTest { underTest.logSuccess("TASK-123"); assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://localhost/dashboard/index/struts") + .contains("ANALYSIS SUCCESSFUL, you can browse https://public/dashboard/index/struts") .contains("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report") - .contains("More about the report processing at https://localhost/api/ce/task?id=TASK-123"); + .contains("More about the report processing at https://public/api/ce/task?id=TASK-123"); File detailsFile = new File(temp.getRoot(), "analysis-details.json"); JsonAssert.assertJson(readFileToString(detailsFile)).isSimilarTo("{" + "\"projectKey\": \"struts\"," + - "\"dashboardUrl\": \"https://localhost/dashboard/index/struts\"," + + "\"dashboardUrl\": \"https://public/dashboard/index/struts\"," + "\"ceTaskId\": \"TASK-123\"," + - "\"ceTaskUrl\": \"https://localhost/api/ce/task?id=TASK-123\"" + + "\"ceTaskUrl\": \"https://public/api/ce/task?id=TASK-123\"" + "}" ); } @Test - public void log_public_url_if_defined() throws IOException { - settings.setProperty(CoreProperties.SERVER_BASE_URL, "https://publicserver/sonarqube"); - ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); - - underTest.logSuccess("TASK-123"); - - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard/index/struts") - .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); - - File detailsFile = new File(temp.getRoot(), "analysis-details.json"); - JsonAssert.assertJson(readFileToString(detailsFile)).isSimilarTo("{" + - "\"projectKey\": \"struts\"," + - "\"dashboardUrl\": \"https://publicserver/sonarqube/dashboard/index/struts\"," + - "\"ceTaskId\": \"TASK-123\"," + - "\"ceTaskUrl\": \"https://publicserver/sonarqube/api/ce/task?id=TASK-123\"" + - "}" - ); - } - - @Test public void log_but_not_dump_information_when_report_is_not_uploaded() { ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java index e0eebd486d3..5d59cf28121 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java @@ -75,7 +75,7 @@ public class DefaultProjectRepositoriesLoaderTest { @Test(expected = IllegalStateException.class) public void failFastHttpError() { - HttpException http = new HttpException("url", 403, "Forbidden"); + HttpException http = new HttpException("url", 403); IllegalStateException e = new IllegalStateException("http error", http); when(wsLoader.loadStream(anyString())).thenThrow(e); loader.load(PROJECT_KEY, false, null); @@ -86,7 +86,7 @@ public class DefaultProjectRepositoriesLoaderTest { thrown.expect(MessageException.class); thrown.expectMessage("http error"); - HttpException http = new HttpException("uri", 403, "Forbidden"); + HttpException http = new HttpException("uri", 403); MessageException e = MessageException.of("http error", http); when(wsLoader.loadStream(anyString())).thenThrow(e); loader.load(PROJECT_KEY, false, null); |