*/
package org.sonar.scanner.bootstrap;
+import java.util.List;
import java.util.Map;
-import org.sonar.api.Plugin;
import org.sonar.api.batch.ScannerSide;
-import org.sonar.core.platform.PluginInfo;
@ScannerSide
public interface PluginInstaller {
* already in local cache.
* @return information about all installed plugins, grouped by key
*/
- Map<String, PluginInfo> installRemotes();
+ Map<String, ScannerPlugin> installRemotes();
/**
- * Used only by tests.
+ * Used only by medium tests.
* @see org.sonar.scanner.mediumtest.ScannerMediumTester
*/
- Map<String, Plugin> installLocals();
+ List<Object[]> installLocals();
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.bootstrap;
+
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.updatecenter.common.Version;
+
+public class ScannerPlugin {
+
+ private final String key;
+ private final long updatedAt;
+ private final PluginInfo info;
+
+ public ScannerPlugin(String key, long updatedAt, PluginInfo info) {
+ this.key = key;
+ this.updatedAt = updatedAt;
+ this.info = info;
+ }
+
+ public PluginInfo getInfo() {
+ return info;
+ }
+
+ public String getName() {
+ return info.getName();
+ }
+
+ public Version getVersion() {
+ return info.getVersion();
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public long getUpdatedAt() {
+ return updatedAt;
+ }
+
+}
import java.io.Reader;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
-import org.sonar.api.Plugin;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
}
@Override
- public Map<String, PluginInfo> installRemotes() {
+ public Map<String, ScannerPlugin> installRemotes() {
return loadPlugins(listInstalledPlugins());
}
- private Map<String, PluginInfo> loadPlugins(InstalledPlugin[] remotePlugins) {
- Map<String, PluginInfo> infosByKey = new HashMap<>(remotePlugins.length);
+ private Map<String, ScannerPlugin> loadPlugins(InstalledPlugin[] remotePlugins) {
+ Map<String, ScannerPlugin> infosByKey = new HashMap<>(remotePlugins.length);
Profiler profiler = Profiler.create(LOG).startDebug("Load plugins");
if (pluginPredicate.apply(installedPlugin.key)) {
File jarFile = download(installedPlugin);
PluginInfo info = PluginInfo.create(jarFile);
- infosByKey.put(info.getKey(), info);
+ infosByKey.put(info.getKey(), new ScannerPlugin(installedPlugin.key, installedPlugin.updatedAt, info));
}
}
}
/**
- * Returns empty on purpose. This method is used only by tests.
+ * Returns empty on purpose. This method is used only by medium tests.
* @see org.sonar.scanner.mediumtest.ScannerMediumTester
*/
@Override
- public Map<String, Plugin> installLocals() {
- return Collections.emptyMap();
+ public List<Object[]> installLocals() {
+ return Collections.emptyList();
}
@VisibleForTesting
String key;
String hash;
String filename;
+ long updatedAt;
}
private class FileDownloader implements FileCache.Downloader {
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Function;
import javax.annotation.CheckForNull;
import org.picocontainer.Startable;
import org.sonar.api.Plugin;
import org.sonar.core.platform.PluginLoader;
import org.sonar.core.platform.PluginRepository;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+
/**
* Orchestrates the installation and loading of plugins
*/
private final PluginLoader loader;
private Map<String, Plugin> pluginInstancesByKeys;
- private Map<String, PluginInfo> infosByKeys;
+ private Map<String, ScannerPlugin> pluginsByKeys;
private Map<ClassLoader, String> keysByClassLoader;
public ScannerPluginRepository(PluginInstaller installer, PluginLoader loader) {
@Override
public void start() {
- infosByKeys = new HashMap<>(installer.installRemotes());
- pluginInstancesByKeys = new HashMap<>(loader.load(infosByKeys));
-
- // this part is only used by tests
- for (Map.Entry<String, Plugin> entry : installer.installLocals().entrySet()) {
- String pluginKey = entry.getKey();
+ pluginsByKeys = new HashMap<>(installer.installRemotes());
+ pluginInstancesByKeys = new HashMap<>(
+ loader.load(pluginsByKeys.values().stream()
+ .map(ScannerPlugin::getInfo)
+ .collect(toMap(PluginInfo::getKey, Function.identity()))));
+
+ // this part is only used by medium tests
+ for (Object[] localPlugin : installer.installLocals()) {
+ String pluginKey = (String) localPlugin[0];
PluginInfo pluginInfo = new PluginInfo(pluginKey);
- infosByKeys.put(pluginKey, pluginInfo);
- pluginInstancesByKeys.put(pluginKey, entry.getValue());
+ pluginsByKeys.put(pluginKey, new ScannerPlugin(pluginInfo.getKey(), (long) localPlugin[2], pluginInfo));
+ pluginInstancesByKeys.put(pluginKey, (Plugin) localPlugin[1]);
}
keysByClassLoader = new HashMap<>();
}
private void logPlugins() {
- if (infosByKeys.isEmpty()) {
+ if (pluginsByKeys.isEmpty()) {
LOG.debug("No plugins loaded");
} else {
LOG.debug("Plugins:");
- for (PluginInfo p : infosByKeys.values()) {
+ for (ScannerPlugin p : pluginsByKeys.values()) {
LOG.debug(" * {} {} ({})", p.getName(), p.getVersion(), p.getKey());
}
}
loader.unload(pluginInstancesByKeys.values());
pluginInstancesByKeys.clear();
- infosByKeys.clear();
+ pluginsByKeys.clear();
keysByClassLoader.clear();
}
+ public Map<String, ScannerPlugin> getPluginsByKey() {
+ return pluginsByKeys;
+ }
+
@Override
public Collection<PluginInfo> getPluginInfos() {
- return infosByKeys.values();
+ return pluginsByKeys.values().stream().map(ScannerPlugin::getInfo).collect(toList());
}
@Override
public PluginInfo getPluginInfo(String key) {
- PluginInfo info = infosByKeys.get(key);
+ ScannerPlugin info = pluginsByKeys.get(key);
Preconditions.checkState(info != null, "Plugin [%s] does not exist", key);
- return info;
+ return info.getInfo();
}
@Override
@Override
public boolean hasPlugin(String key) {
- return infosByKeys.containsKey(key);
+ return pluginsByKeys.containsKey(key);
}
}
package org.sonar.scanner.mediumtest;
import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.sonar.api.Plugin;
import org.sonar.core.platform.PluginInfo;
import org.sonar.scanner.bootstrap.PluginInstaller;
+import org.sonar.scanner.bootstrap.ScannerPlugin;
public class FakePluginInstaller implements PluginInstaller {
- private final Map<String, PluginInfo> infosByKeys = new HashMap<>();
- private final Map<String, Plugin> instancesByKeys = new HashMap<>();
+ private final Map<String, ScannerPlugin> pluginsByKeys = new HashMap<>();
+ private final List<Object[]> mediumTestPlugins = new ArrayList<>();
- public FakePluginInstaller add(String pluginKey, File jarFile) {
- infosByKeys.put(pluginKey, PluginInfo.create(jarFile));
+ public FakePluginInstaller add(String pluginKey, File jarFile, long lastUpdatedAt) {
+ pluginsByKeys.put(pluginKey, new ScannerPlugin(pluginKey, lastUpdatedAt, PluginInfo.create(jarFile)));
return this;
}
- public FakePluginInstaller add(String pluginKey, Plugin instance) {
- instancesByKeys.put(pluginKey, instance);
+ public FakePluginInstaller add(String pluginKey, Plugin instance, long lastUpdatedAt) {
+ mediumTestPlugins.add(new Object[] {pluginKey, instance, lastUpdatedAt});
return this;
}
@Override
- public Map<String, PluginInfo> installRemotes() {
- return infosByKeys;
+ public Map<String, ScannerPlugin> installRemotes() {
+ return pluginsByKeys;
}
@Override
- public Map<String, Plugin> installLocals() {
- return instancesByKeys;
+ public List<Object[]> installLocals() {
+ return mediumTestPlugins;
}
}
*/
package org.sonar.scanner.report;
+import java.util.Map.Entry;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.config.Configuration;
import org.sonar.scanner.ProjectAnalysisInfo;
+import org.sonar.scanner.bootstrap.ScannerPlugin;
+import org.sonar.scanner.bootstrap.ScannerPluginRepository;
import org.sonar.scanner.cpd.CpdSettings;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
private final InputModuleHierarchy moduleHierarchy;
private final CpdSettings cpdSettings;
private final AnalysisMode mode;
+ private final ScannerPluginRepository pluginRepository;
public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings,
- ModuleQProfiles qProfiles, CpdSettings cpdSettings, AnalysisMode mode) {
+ ModuleQProfiles qProfiles, CpdSettings cpdSettings, AnalysisMode mode, ScannerPluginRepository pluginRepository) {
this.projectAnalysisInfo = projectAnalysisInfo;
this.moduleHierarchy = moduleHierarchy;
this.settings = settings;
this.qProfiles = qProfiles;
this.cpdSettings = cpdSettings;
this.mode = mode;
+ this.pluginRepository = pluginRepository;
}
@Override
.setName(qp.getName())
.setRulesUpdatedAt(qp.getRulesUpdatedAt().getTime()).build());
}
+ for (Entry<String, ScannerPlugin> pluginEntry : pluginRepository.getPluginsByKey().entrySet()) {
+ builder.getMutablePluginsByKey().put(pluginEntry.getKey(), ScannerReport.Metadata.Plugin.newBuilder()
+ .setKey(pluginEntry.getKey())
+ .setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build());
+ }
writer.writeMetadata(builder.build());
}
}
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyCollectionOf;
+import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Test
public void install_and_load_plugins() {
PluginInfo info = new PluginInfo("squid");
- ImmutableMap<String, PluginInfo> infos = ImmutableMap.of("squid", info);
+ ImmutableMap<String, ScannerPlugin> plugins = ImmutableMap.of("squid", new ScannerPlugin("squid", 1L, info));
Plugin instance = mock(Plugin.class);
- when(loader.load(infos)).thenReturn(ImmutableMap.of("squid", instance));
- when(installer.installRemotes()).thenReturn(infos);
+ when(loader.load(anyMapOf(String.class, PluginInfo.class))).thenReturn(ImmutableMap.of("squid", instance));
+ when(installer.installRemotes()).thenReturn(plugins);
underTest.start();
assertThat(underTest.getPluginInfos()).containsOnly(info);
+ assertThat(underTest.getPluginsByKey()).isEqualTo(plugins);
assertThat(underTest.getPluginInfo("squid")).isSameAs(info);
assertThat(underTest.getPluginInstance("squid")).isSameAs(instance);
}
public ScannerMediumTester registerPlugin(String pluginKey, File location) {
- pluginInstaller.add(pluginKey, location);
+ return registerPlugin(pluginKey, location, 1L);
+ }
+
+ public ScannerMediumTester registerPlugin(String pluginKey, File location, long lastUpdatedAt) {
+ pluginInstaller.add(pluginKey, location, lastUpdatedAt);
return this;
}
public ScannerMediumTester registerPlugin(String pluginKey, Plugin instance) {
- pluginInstaller.add(pluginKey, instance);
+ return registerPlugin(pluginKey, instance, 1L);
+ }
+
+ public ScannerMediumTester registerPlugin(String pluginKey, Plugin instance, long lastUpdatedAt) {
+ pluginInstaller.add(pluginKey, instance, lastUpdatedAt);
return this;
}
*/
package org.sonar.scanner.report;
+import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.scanner.ProjectAnalysisInfo;
+import org.sonar.scanner.bootstrap.ScannerPlugin;
+import org.sonar.scanner.bootstrap.ScannerPluginRepository;
import org.sonar.scanner.cpd.CpdSettings;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportReader;
import org.sonar.scanner.rule.QProfile;
import static java.util.Arrays.asList;
+import static java.util.Collections.emptyMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock;
private CpdSettings cpdSettings;
private InputModuleHierarchy inputModuleHierarchy;
private AnalysisMode analysisMode;
+ private ScannerPluginRepository pluginRepository;
@Before
public void prepare() throws IOException {
when(projectAnalysisInfo.analysisDate()).thenReturn(new Date(1234567L));
settings = new MapSettings();
qProfiles = mock(ModuleQProfiles.class);
+ pluginRepository = mock(ScannerPluginRepository.class);
createPublisher(ProjectDefinition.create().setKey("foo"));
+ when(pluginRepository.getPluginsByKey()).thenReturn(emptyMap());
}
private void createPublisher(ProjectDefinition def) throws IOException {
inputModuleHierarchy = mock(InputModuleHierarchy.class);
when(inputModuleHierarchy.root()).thenReturn(rootModule);
analysisMode = mock(AnalysisMode.class);
- underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, settings.asConfig(), qProfiles, cpdSettings, analysisMode);
+ underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, settings.asConfig(), qProfiles, cpdSettings, analysisMode, pluginRepository);
}
@Test
settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
Date date = new Date();
when(qProfiles.findAll()).thenReturn(asList(new QProfile("q1", "Q1", "java", date)));
+ when(pluginRepository.getPluginsByKey()).thenReturn(ImmutableMap.of(
+ "java", new ScannerPlugin("java", 12345L, null),
+ "php", new ScannerPlugin("php", 45678L, null)));
File outputDir = temp.newFolder();
ScannerReportWriter writer = new ScannerReportWriter(outputDir);
.setLanguage("java")
.setRulesUpdatedAt(date.getTime())
.build()));
+ assertThat(metadata.getPluginsByKey()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin.newBuilder()
+ .setKey("java")
+ .setUpdatedAt(12345)
+ .build()),
+ entry("php", org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin.newBuilder()
+ .setKey("php")
+ .setUpdatedAt(45678)
+ .build()));
}
@Test
int32 root_component_ref = 5;
bool cross_project_duplication_activated = 6;
map<string, QProfile> qprofiles_per_language = 7;
- bool incremental = 8;
+ map<string, Plugin> plugins_by_key = 8;
+ bool incremental = 9;
message QProfile {
string key = 1;
string language = 3;
int64 rulesUpdatedAt = 4;
}
+
+ message Plugin {
+ string key = 1;
+ int64 updatedAt = 2;
+ }
}
message ContextProperty {