diff options
Diffstat (limited to 'sonar-scanner-engine/src/main/java/org/sonar/scanner/cache')
8 files changed, 449 insertions, 0 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheEnabled.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheEnabled.java new file mode 100644 index 00000000000..b93150ae944 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheEnabled.java @@ -0,0 +1,35 @@ +/* + * 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 org.sonar.api.config.Configuration; + +public class AnalysisCacheEnabled { + static final String PROP_KEY = "sonar.analysisCache.enabled"; + private final Configuration configuration; + + public AnalysisCacheEnabled(Configuration configuration) { + this.configuration = configuration; + } + + public boolean isEnabled() { + return configuration.getBoolean(PROP_KEY).orElse(false); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java new file mode 100644 index 00000000000..420a5d5f852 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java @@ -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.net.HttpURLConnection; +import java.util.Optional; +import java.util.zip.InflaterInputStream; +import org.sonar.api.scanner.fs.InputProject; +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.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/scanner_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.branchType() == BranchType.BRANCH && branchConfiguration.branchName() != null) { + url = url + "&branch=" + branchConfiguration.branchName(); + } + + GetRequest request = new GetRequest(url).setHeader(ACCEPT_ENCODING, "gzip"); + + try (WsResponse response = wsClient.call(request)) { + if (response.code() == HttpURLConnection.HTTP_NOT_FOUND) { + return Optional.empty(); + } + try (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 (IOException e) { + throw new IllegalStateException("Failed to download 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 plugin cache", e); + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheMemoryStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheMemoryStorage.java new file mode 100644 index 00000000000..420bd1711a8 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheMemoryStorage.java @@ -0,0 +1,59 @@ +/* + * 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.InputStream; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.scanner.protocol.internal.ScannerInternal.AnalysisCacheMsg; + +public class AnalysisCacheMemoryStorage implements AnalysisCacheStorage { + private final AnalysisCacheLoader loader; + @Nullable + private AnalysisCacheMsg cache; + + public AnalysisCacheMemoryStorage(AnalysisCacheLoader loader) { + this.loader = loader; + } + + @Override + @CheckForNull + public InputStream get(String key) { + if (cache == null) { + return null; + } + if (cache.containsMap(key)) { + return cache.getMapOrThrow(key).newInput(); + } + return null; + } + + @Override + public boolean contains(String key) { + if (cache == null) { + return false; + } + return cache.containsMap(key); + } + + public void load() { + cache = loader.load().orElse(null); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheProvider.java new file mode 100644 index 00000000000..b340261f569 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheProvider.java @@ -0,0 +1,83 @@ +/* + * 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.InputStream; +import java.util.Map; +import org.jetbrains.annotations.Nullable; +import org.sonar.api.batch.sensor.cache.ReadCache; +import org.springframework.context.annotation.Bean; + +import static java.util.Collections.emptyMap; + +public class AnalysisCacheProvider { + @Bean("ReadCache") + public ReadCache provideReader(AnalysisCacheEnabled analysisCacheEnabled, AnalysisCacheMemoryStorage storage) { + if (analysisCacheEnabled.isEnabled()) { + storage.load(); + return new ReadCacheImpl(storage); + } + return new NoOpReadCache(); + } + + @Bean("WriteCache") + public ScannerWriteCache provideWriter(AnalysisCacheEnabled analysisCacheEnabled, ReadCache readCache) { + if (analysisCacheEnabled.isEnabled()) { + return new WriteCacheImpl(readCache); + } + return new NoOpWriteCache(); + } + + + static class NoOpWriteCache implements ScannerWriteCache { + @Override + public void write(String s, InputStream inputStream) { + // no op + } + + @Override + public void write(String s, byte[] bytes) { + // no op + } + + @Override + public void copyFromPrevious(String s) { + // no op + } + + @Override + public Map<String, byte[]> getCache() { + return emptyMap(); + } + } + + static class NoOpReadCache implements ReadCache { + @Nullable + @Override + public InputStream read(String s) { + return null; + } + + @Override + public boolean contains(String s) { + return false; + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheStorage.java new file mode 100644 index 00000000000..5fb7765da38 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheStorage.java @@ -0,0 +1,30 @@ +/* + * 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.InputStream; +import javax.annotation.CheckForNull; + +public interface AnalysisCacheStorage { + @CheckForNull + InputStream get(String key); + + boolean contains(String key); +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/ReadCacheImpl.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/ReadCacheImpl.java new file mode 100644 index 00000000000..4a94e0ae7b0 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/ReadCacheImpl.java @@ -0,0 +1,47 @@ +/* + * 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.InputStream; +import org.sonar.api.batch.sensor.cache.ReadCache; + +import static org.sonar.api.utils.Preconditions.checkArgument; +import static org.sonar.api.utils.Preconditions.checkNotNull; + +public class ReadCacheImpl implements ReadCache { + private final AnalysisCacheStorage cache; + + public ReadCacheImpl(AnalysisCacheStorage storage) { + this.cache = storage; + } + + @Override + public InputStream read(String key) { + checkNotNull(key); + checkArgument(contains(key)); + return cache.get(key); + } + + @Override + public boolean contains(String key) { + checkNotNull(key); + return cache.contains(key); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/ScannerWriteCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/ScannerWriteCache.java new file mode 100644 index 00000000000..4087db134b5 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/ScannerWriteCache.java @@ -0,0 +1,27 @@ +/* + * 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.util.Map; +import org.sonar.api.batch.sensor.cache.WriteCache; + +public interface ScannerWriteCache extends WriteCache { + Map<String, byte[]> getCache(); +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/WriteCacheImpl.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/WriteCacheImpl.java new file mode 100644 index 00000000000..adeb6216d23 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/WriteCacheImpl.java @@ -0,0 +1,81 @@ +/* + * 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.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.sonar.api.batch.sensor.cache.ReadCache; + +import static java.util.Collections.unmodifiableMap; +import static org.sonar.api.utils.Preconditions.checkArgument; +import static org.sonar.api.utils.Preconditions.checkNotNull; + +public class WriteCacheImpl implements ScannerWriteCache { + private final ReadCache readCache; + private final Map<String, byte[]> cache = new HashMap<>(); + + public WriteCacheImpl(ReadCache readCache) { + this.readCache = readCache; + } + + @Override + public void write(String key, InputStream data) { + checkNotNull(data); + checkKey(key); + try { + byte[] arr = data.readAllBytes(); + cache.put(key, arr); + } catch (IOException e) { + throw new IllegalStateException("Failed to read stream", e); + } + } + + @Override + public void write(String key, byte[] data) { + checkNotNull(data); + checkKey(key); + cache.put(key, Arrays.copyOf(data, data.length)); + } + + @Override + public void copyFromPrevious(String key) { + checkArgument(readCache.contains(key), "Previous cache doesn't contain key '%s'", key); + checkKey(key); + + try { + cache.put(key, readCache.read(key).readAllBytes()); + } catch (IOException e) { + throw new IllegalStateException("Failed to read plugin cache for key " + key, e); + } + } + + @Override + public Map<String, byte[]> getCache() { + return unmodifiableMap(cache); + } + + private void checkKey(String key) { + checkNotNull(key); + checkArgument(!cache.containsKey(key), "Cache already contains key '%s'", key); + } +} |