} | } | ||||
public boolean isEnabled() { | public boolean isEnabled() { | ||||
return configuration.getBoolean(PROP_KEY).orElse(false); | |||||
return configuration.getBoolean(PROP_KEY).orElse(true); | |||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.scanner.cache; | package org.sonar.scanner.cache; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Optional; | 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; | ||||
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(); | |||||
} | } |
/* | |||||
* 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); | |||||
} | |||||
} | |||||
} |
import org.sonar.scanner.bootstrap.GlobalAnalysisMode; | import org.sonar.scanner.bootstrap.GlobalAnalysisMode; | ||||
import org.sonar.scanner.bootstrap.PostJobExtensionDictionary; | import org.sonar.scanner.bootstrap.PostJobExtensionDictionary; | ||||
import org.sonar.scanner.cache.AnalysisCacheEnabled; | 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.AnalysisCacheMemoryStorage; | ||||
import org.sonar.scanner.cache.AnalysisCacheProvider; | import org.sonar.scanner.cache.AnalysisCacheProvider; | ||||
import org.sonar.scanner.ci.CiConfigurationProvider; | import org.sonar.scanner.ci.CiConfigurationProvider; | ||||
// Plugin cache | // Plugin cache | ||||
AnalysisCacheProvider.class, | AnalysisCacheProvider.class, | ||||
AnalysisCacheMemoryStorage.class, | AnalysisCacheMemoryStorage.class, | ||||
AnalysisCacheLoader.class, | |||||
DefaultAnalysisCacheLoader.class, | |||||
// Report | // Report | ||||
ReferenceBranchSupplier.class, | ReferenceBranchSupplier.class, |
private final AnalysisCacheEnabled analysisCacheEnabled = new AnalysisCacheEnabled(configuration); | private final AnalysisCacheEnabled analysisCacheEnabled = new AnalysisCacheEnabled(configuration); | ||||
@Test | @Test | ||||
public void disabled_unless_property_set() { | |||||
assertThat(analysisCacheEnabled.isEnabled()).isFalse(); | |||||
public void enabled_by_default() { | |||||
assertThat(analysisCacheEnabled.isEnabled()).isTrue(); | |||||
} | } | ||||
@Test | @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(); | |||||
} | } | ||||
} | } |
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||
import static org.mockito.Mockito.when; | 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() | private final static AnalysisCacheMsg MSG = AnalysisCacheMsg.newBuilder() | ||||
.putMap("key", ByteString.copyFrom("value", StandardCharsets.UTF_8)) | .putMap("key", ByteString.copyFrom("value", StandardCharsets.UTF_8)) | ||||
.build(); | .build(); | ||||
private final DefaultScannerWsClient wsClient = mock(DefaultScannerWsClient.class); | private final DefaultScannerWsClient wsClient = mock(DefaultScannerWsClient.class); | ||||
private final InputProject project = mock(InputProject.class); | private final InputProject project = mock(InputProject.class); | ||||
private final BranchConfiguration branchConfiguration = mock(BranchConfiguration.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 | @Before | ||||
public void before() { | public void before() { |
import java.util.LinkedList; | import java.util.LinkedList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Optional; | |||||
import java.util.Properties; | import java.util.Properties; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.api.SonarQubeSide; | import org.sonar.api.SonarQubeSide; | ||||
import org.sonar.api.SonarRuntime; | import org.sonar.api.SonarRuntime; | ||||
import org.sonar.api.batch.rule.LoadedActiveRule; | 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.impl.server.RulesDefinitionContext; | ||||
import org.sonar.api.measures.CoreMetrics; | import org.sonar.api.measures.CoreMetrics; | ||||
import org.sonar.api.measures.Metric; | import org.sonar.api.measures.Metric; | ||||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | import org.sonar.batch.bootstrapper.EnvironmentInformation; | ||||
import org.sonar.batch.bootstrapper.LogOutput; | import org.sonar.batch.bootstrapper.LogOutput; | ||||
import org.sonar.scanner.bootstrap.GlobalAnalysisMode; | 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.report.CeTaskReportDataHolder; | ||||
import org.sonar.scanner.repository.FileData; | import org.sonar.scanner.repository.FileData; | ||||
import org.sonar.scanner.repository.MetricsRepository; | import org.sonar.scanner.repository.MetricsRepository; | ||||
import org.sonarqube.ws.NewCodePeriods; | import org.sonarqube.ws.NewCodePeriods; | ||||
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | ||||
import org.sonarqube.ws.Rules.ListResponse.Rule; | import org.sonarqube.ws.Rules.ListResponse.Rule; | ||||
import org.springframework.context.annotation.Bean; | |||||
import static java.util.Collections.emptySet; | import static java.util.Collections.emptySet; | ||||
private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader(); | private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader(); | ||||
private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader(); | private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader(); | ||||
private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader(); | private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader(); | ||||
private final FakeAnalysisCacheLoader analysisCacheLoader = new FakeAnalysisCacheLoader(); | |||||
private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); | private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); | ||||
private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); | private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); | ||||
private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); | private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); | ||||
tester.globalSettingsLoader, | tester.globalSettingsLoader, | ||||
tester.projectSettingsLoader, | tester.projectSettingsLoader, | ||||
tester.newCodePeriodLoader, | tester.newCodePeriodLoader, | ||||
tester.analysisCacheLoader, | |||||
tester.sonarRuntime, | tester.sonarRuntime, | ||||
tester.reportMetadataHolder, | tester.reportMetadataHolder, | ||||
result) | result) | ||||
} | } | ||||
} | } | ||||
@Priority(1) | |||||
private static class FakeAnalysisCacheLoader implements AnalysisCacheLoader { | |||||
@Override | |||||
public Optional<ScannerInternal.AnalysisCacheMsg> load() { | |||||
return Optional.empty(); | |||||
} | |||||
} | |||||
@Priority(1) | @Priority(1) | ||||
private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader { | private static class FakeGlobalSettingsLoader implements GlobalSettingsLoader { | ||||