@@ -30,6 +30,6 @@ public class AnalysisCacheEnabled { | |||
} | |||
public boolean isEnabled() { | |||
return configuration.getBoolean(PROP_KEY).orElse(false); | |||
return configuration.getBoolean(PROP_KEY).orElse(true); | |||
} | |||
} |
@@ -19,70 +19,9 @@ | |||
*/ | |||
package org.sonar.scanner.cache; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Optional; | |||
import java.util.zip.InflaterInputStream; | |||
import org.sonar.api.scanner.fs.InputProject; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.core.util.Protobuf; | |||
import org.sonar.scanner.bootstrap.DefaultScannerWsClient; | |||
import org.sonar.scanner.protocol.internal.ScannerInternal; | |||
import org.sonar.scanner.protocol.internal.ScannerInternal.AnalysisCacheMsg; | |||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||
import org.sonar.scanner.scan.branch.BranchType; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.HttpException; | |||
import org.sonarqube.ws.client.WsResponse; | |||
/** | |||
* Loads plugin cache into the local storage | |||
*/ | |||
public class AnalysisCacheLoader { | |||
static final String CONTENT_ENCODING = "Content-Encoding"; | |||
static final String ACCEPT_ENCODING = "Accept-Encoding"; | |||
private static final String URL = "api/analysis_cache/get"; | |||
private final DefaultScannerWsClient wsClient; | |||
private final InputProject project; | |||
private final BranchConfiguration branchConfiguration; | |||
public AnalysisCacheLoader(DefaultScannerWsClient wsClient, InputProject project, BranchConfiguration branchConfiguration) { | |||
this.project = project; | |||
this.branchConfiguration = branchConfiguration; | |||
this.wsClient = wsClient; | |||
} | |||
public Optional<AnalysisCacheMsg> load() { | |||
String url = URL + "?project=" + project.key(); | |||
if (branchConfiguration.referenceBranchName() != null) { | |||
url = url + "&branch=" + branchConfiguration.referenceBranchName(); | |||
} | |||
GetRequest request = new GetRequest(url).setHeader(ACCEPT_ENCODING, "gzip"); | |||
try (WsResponse response = wsClient.call(request); InputStream is = response.contentStream()) { | |||
Optional<String> contentEncoding = response.header(CONTENT_ENCODING); | |||
if (contentEncoding.isPresent() && contentEncoding.get().equals("gzip")) { | |||
return Optional.of(decompress(is)); | |||
} else { | |||
return Optional.of(Protobuf.read(is, AnalysisCacheMsg.parser())); | |||
} | |||
} catch (HttpException e) { | |||
if (e.code() == 404) { | |||
return Optional.empty(); | |||
} | |||
throw MessageException.of("Failed to download analysis cache: " + DefaultScannerWsClient.createErrorMessage(e)); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Failed to download analysis cache", e); | |||
} | |||
} | |||
private static AnalysisCacheMsg decompress(InputStream is) { | |||
try (InflaterInputStream iis = new InflaterInputStream(is)) { | |||
return Protobuf.read(iis, ScannerInternal.AnalysisCacheMsg.parser()); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to decompress analysis cache", e); | |||
} | |||
} | |||
public interface AnalysisCacheLoader { | |||
Optional<ScannerInternal.AnalysisCacheMsg> load(); | |||
} |
@@ -0,0 +1,87 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.scanner.cache; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Optional; | |||
import java.util.zip.InflaterInputStream; | |||
import org.sonar.api.scanner.fs.InputProject; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.core.util.Protobuf; | |||
import org.sonar.scanner.bootstrap.DefaultScannerWsClient; | |||
import org.sonar.scanner.protocol.internal.ScannerInternal; | |||
import org.sonar.scanner.protocol.internal.ScannerInternal.AnalysisCacheMsg; | |||
import org.sonar.scanner.scan.branch.BranchConfiguration; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.HttpException; | |||
import org.sonarqube.ws.client.WsResponse; | |||
/** | |||
* Loads plugin cache into the local storage | |||
*/ | |||
public class DefaultAnalysisCacheLoader implements AnalysisCacheLoader { | |||
static final String CONTENT_ENCODING = "Content-Encoding"; | |||
static final String ACCEPT_ENCODING = "Accept-Encoding"; | |||
private static final String URL = "api/analysis_cache/get"; | |||
private final DefaultScannerWsClient wsClient; | |||
private final InputProject project; | |||
private final BranchConfiguration branchConfiguration; | |||
public DefaultAnalysisCacheLoader(DefaultScannerWsClient wsClient, InputProject project, BranchConfiguration branchConfiguration) { | |||
this.project = project; | |||
this.branchConfiguration = branchConfiguration; | |||
this.wsClient = wsClient; | |||
} | |||
@Override public Optional<AnalysisCacheMsg> load() { | |||
String url = URL + "?project=" + project.key(); | |||
if (branchConfiguration.referenceBranchName() != null) { | |||
url = url + "&branch=" + branchConfiguration.referenceBranchName(); | |||
} | |||
GetRequest request = new GetRequest(url).setHeader(ACCEPT_ENCODING, "gzip"); | |||
try (WsResponse response = wsClient.call(request); InputStream is = response.contentStream()) { | |||
Optional<String> contentEncoding = response.header(CONTENT_ENCODING); | |||
if (contentEncoding.isPresent() && contentEncoding.get().equals("gzip")) { | |||
return Optional.of(decompress(is)); | |||
} else { | |||
return Optional.of(Protobuf.read(is, AnalysisCacheMsg.parser())); | |||
} | |||
} catch (HttpException e) { | |||
if (e.code() == 404) { | |||
return Optional.empty(); | |||
} | |||
throw MessageException.of("Failed to download analysis cache: " + DefaultScannerWsClient.createErrorMessage(e)); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Failed to download analysis cache", e); | |||
} | |||
} | |||
private static AnalysisCacheMsg decompress(InputStream is) { | |||
try (InflaterInputStream iis = new InflaterInputStream(is)) { | |||
return Protobuf.read(iis, ScannerInternal.AnalysisCacheMsg.parser()); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Failed to decompress analysis cache", e); | |||
} | |||
} | |||
} |
@@ -44,7 +44,7 @@ import org.sonar.scanner.bootstrap.ExtensionMatcher; | |||
import org.sonar.scanner.bootstrap.GlobalAnalysisMode; | |||
import org.sonar.scanner.bootstrap.PostJobExtensionDictionary; | |||
import org.sonar.scanner.cache.AnalysisCacheEnabled; | |||
import org.sonar.scanner.cache.AnalysisCacheLoader; | |||
import org.sonar.scanner.cache.DefaultAnalysisCacheLoader; | |||
import org.sonar.scanner.cache.AnalysisCacheMemoryStorage; | |||
import org.sonar.scanner.cache.AnalysisCacheProvider; | |||
import org.sonar.scanner.ci.CiConfigurationProvider; | |||
@@ -232,7 +232,7 @@ public class SpringProjectScanContainer extends SpringComponentContainer { | |||
// Plugin cache | |||
AnalysisCacheProvider.class, | |||
AnalysisCacheMemoryStorage.class, | |||
AnalysisCacheLoader.class, | |||
DefaultAnalysisCacheLoader.class, | |||
// Report | |||
ReferenceBranchSupplier.class, |
@@ -33,13 +33,13 @@ public class AnalysisCacheEnabledTest { | |||
private final AnalysisCacheEnabled analysisCacheEnabled = new AnalysisCacheEnabled(configuration); | |||
@Test | |||
public void disabled_unless_property_set() { | |||
assertThat(analysisCacheEnabled.isEnabled()).isFalse(); | |||
public void enabled_by_default() { | |||
assertThat(analysisCacheEnabled.isEnabled()).isTrue(); | |||
} | |||
@Test | |||
public void enabled_if_property_set() { | |||
when(configuration.getBoolean(PROP_KEY)).thenReturn(Optional.of(true)); | |||
assertThat(analysisCacheEnabled.isEnabled()).isTrue(); | |||
public void disabled_if_property_set() { | |||
when(configuration.getBoolean(PROP_KEY)).thenReturn(Optional.of(false)); | |||
assertThat(analysisCacheEnabled.isEnabled()).isFalse(); | |||
} | |||
} |
@@ -45,9 +45,9 @@ import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.scanner.cache.AnalysisCacheLoader.CONTENT_ENCODING; | |||
import static org.sonar.scanner.cache.DefaultAnalysisCacheLoader.CONTENT_ENCODING; | |||
public class AnalysisCacheLoaderTest { | |||
public class DefaultAnalysisCacheLoaderTest { | |||
private final static AnalysisCacheMsg MSG = AnalysisCacheMsg.newBuilder() | |||
.putMap("key", ByteString.copyFrom("value", StandardCharsets.UTF_8)) | |||
.build(); | |||
@@ -55,7 +55,7 @@ public class AnalysisCacheLoaderTest { | |||
private final DefaultScannerWsClient wsClient = mock(DefaultScannerWsClient.class); | |||
private final InputProject project = mock(InputProject.class); | |||
private final BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); | |||
private final AnalysisCacheLoader loader = new AnalysisCacheLoader(wsClient, project, branchConfiguration); | |||
private final DefaultAnalysisCacheLoader loader = new DefaultAnalysisCacheLoader(wsClient, project, branchConfiguration); | |||
@Before | |||
public void before() { |
@@ -33,6 +33,7 @@ import java.util.HashMap; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Properties; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
@@ -45,6 +46,7 @@ import org.sonar.api.SonarProduct; | |||
import org.sonar.api.SonarQubeSide; | |||
import org.sonar.api.SonarRuntime; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.api.batch.sensor.cache.ReadCache; | |||
import org.sonar.api.impl.server.RulesDefinitionContext; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Metric; | |||
@@ -57,6 +59,10 @@ import org.sonar.batch.bootstrapper.Batch; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonar.batch.bootstrapper.LogOutput; | |||
import org.sonar.scanner.bootstrap.GlobalAnalysisMode; | |||
import org.sonar.scanner.cache.AnalysisCacheLoader; | |||
import org.sonar.scanner.cache.AnalysisCacheMemoryStorage; | |||
import org.sonar.scanner.cache.ReadCacheImpl; | |||
import org.sonar.scanner.protocol.internal.ScannerInternal; | |||
import org.sonar.scanner.report.CeTaskReportDataHolder; | |||
import org.sonar.scanner.repository.FileData; | |||
import org.sonar.scanner.repository.MetricsRepository; | |||
@@ -79,6 +85,7 @@ import org.sonar.scanner.scan.branch.ProjectPullRequests; | |||
import org.sonarqube.ws.NewCodePeriods; | |||
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | |||
import org.sonarqube.ws.Rules.ListResponse.Rule; | |||
import org.springframework.context.annotation.Bean; | |||
import static java.util.Collections.emptySet; | |||
@@ -97,6 +104,7 @@ public class ScannerMediumTester extends ExternalResource { | |||
private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader(); | |||
private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader(); | |||
private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader(); | |||
private final FakeAnalysisCacheLoader analysisCacheLoader = new FakeAnalysisCacheLoader(); | |||
private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); | |||
private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); | |||
private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); | |||
@@ -307,6 +315,7 @@ public class ScannerMediumTester extends ExternalResource { | |||
tester.globalSettingsLoader, | |||
tester.projectSettingsLoader, | |||
tester.newCodePeriodLoader, | |||
tester.analysisCacheLoader, | |||
tester.sonarRuntime, | |||
tester.reportMetadataHolder, | |||
result) | |||
@@ -518,6 +527,14 @@ public class ScannerMediumTester extends ExternalResource { | |||
} | |||
} | |||
@Priority(1) | |||
private static class FakeAnalysisCacheLoader implements AnalysisCacheLoader { | |||
@Override | |||
public Optional<ScannerInternal.AnalysisCacheMsg> load() { | |||
return Optional.empty(); | |||
} | |||
} | |||
@Priority(1) | |||
private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader { | |||