diff options
25 files changed, 297 insertions, 74 deletions
diff --git a/it/it-plugins/batch-plugin/src/main/java/com/sonarsource/BatchPlugin.java b/it/it-plugins/batch-plugin/src/main/java/com/sonarsource/BatchPlugin.java index 2be444b22a3..5bc5ee622af 100644 --- a/it/it-plugins/batch-plugin/src/main/java/com/sonarsource/BatchPlugin.java +++ b/it/it-plugins/batch-plugin/src/main/java/com/sonarsource/BatchPlugin.java @@ -23,14 +23,15 @@ import com.sonarsource.decimal_scale_of_measures.DecimalScaleMeasureComputer; import com.sonarsource.decimal_scale_of_measures.DecimalScaleMetric; import com.sonarsource.decimal_scale_of_measures.DecimalScaleProperty; import com.sonarsource.decimal_scale_of_measures.DecimalScaleSensor; -import java.util.Arrays; -import java.util.List; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; -public class BatchPlugin extends SonarPlugin { +import static java.util.Arrays.asList; - public List getExtensions() { - return Arrays.asList( +public class BatchPlugin implements Plugin { + + @Override + public void define(Context context) { + context.addExtensions(asList( // SONAR-6939 decimal_scale_of_measures DecimalScaleMeasureComputer.class, DecimalScaleMetric.class, @@ -40,7 +41,7 @@ public class BatchPlugin extends SonarPlugin { DumpSettingsInitializer.class, RaiseMessageException.class, TempFolderExtension.class, - WaitingSensor.class); + WaitingSensor.class + )); } - } diff --git a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java index 1ce79e55b0f..fdaffcfed8d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; import org.picocontainer.Startable; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; import org.sonar.api.server.ServerSide; import org.sonar.core.platform.PluginInfo; @@ -88,7 +89,7 @@ public class DebtModelPluginRepository implements Startable { contributingPluginKeyToClassLoader.put(DEFAULT_MODEL, getClass().getClassLoader()); for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { String pluginKey = pluginInfo.getKey(); - SonarPlugin plugin = pluginRepository.getPluginInstance(pluginKey); + Plugin plugin = pluginRepository.getPluginInstance(pluginKey); ClassLoader classLoader = plugin.getClass().getClassLoader(); if (classLoader.getResource(getXMLFilePath(pluginKey)) != null) { contributingPluginKeyToClassLoader.put(pluginKey, classLoader); diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java index 0e77c0f49ca..9538f2cf600 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java +++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationDispatcherMetadata.java @@ -28,7 +28,7 @@ import org.sonar.api.server.ServerSide; * Notification dispatchers (see {@link NotificationDispatcher}) can define their own metadata class in order * to tell more about them. * <p/> - * Instances of these classes must be declared in {@link org.sonar.api.SonarPlugin#getExtensions()}. + * Instances of these classes must be declared by {@link org.sonar.api.Plugin}. */ @ServerSide public final class NotificationDispatcherMetadata { diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java b/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java index ee56ab6226d..2a5d0a3e7ad 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java @@ -27,6 +27,7 @@ import javax.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; import org.sonar.api.platform.ServerFileSystem; import org.sonar.api.utils.log.Logger; @@ -60,7 +61,7 @@ public class RailsAppsDeployer implements Startable { for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { String pluginKey = pluginInfo.getKey(); - SonarPlugin plugin = pluginRepository.getPluginInstance(pluginKey); + Plugin plugin = pluginRepository.getPluginInstance(pluginKey); try { deployRailsApp(appsDir, pluginKey, plugin.getClass().getClassLoader()); } catch (Exception e) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java index eb36db275d1..53fe385e2e3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerExtensionInstaller.java @@ -24,7 +24,9 @@ import com.google.common.collect.ListMultimap; import java.util.Map; import org.sonar.api.Extension; import org.sonar.api.ExtensionProvider; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; +import org.sonar.api.SonarQubeVersion; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.AnnotationUtils; import org.sonar.core.platform.ComponentContainer; @@ -37,9 +39,11 @@ import org.sonar.core.platform.PluginRepository; @ServerSide public class ServerExtensionInstaller { + private final SonarQubeVersion sonarQubeVersion; private final PluginRepository pluginRepository; - public ServerExtensionInstaller(PluginRepository pluginRepository) { + public ServerExtensionInstaller(SonarQubeVersion sonarQubeVersion, PluginRepository pluginRepository) { + this.sonarQubeVersion = sonarQubeVersion; this.pluginRepository = pluginRepository; } @@ -49,10 +53,12 @@ public class ServerExtensionInstaller { for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { try { String pluginKey = pluginInfo.getKey(); - SonarPlugin plugin = pluginRepository.getPluginInstance(pluginKey); + Plugin plugin = pluginRepository.getPluginInstance(pluginKey); container.addExtension(pluginInfo, plugin); - for (Object extension : plugin.getExtensions()) { + Plugin.Context context = new Plugin.Context(sonarQubeVersion); + plugin.define(context); + for (Object extension : context.getExtensions()) { if (installExtension(container, pluginInfo, extension, true) != null) { installedExtensionsByPlugin.put(pluginInfo, extension); } else { diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java index 40bcc6b20dc..66729394485 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java @@ -37,7 +37,7 @@ import java.util.Set; import javax.annotation.Nonnull; import org.apache.commons.io.FileUtils; import org.picocontainer.Startable; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.api.platform.Server; import org.sonar.api.platform.ServerUpgradeStatus; import org.sonar.api.utils.MessageException; @@ -53,10 +53,10 @@ import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Lists.newArrayList; import static java.lang.String.format; import static org.apache.commons.io.FileUtils.copyFile; -import static org.sonar.core.util.FileUtils.deleteQuietly; import static org.apache.commons.io.FileUtils.moveFile; import static org.apache.commons.io.FileUtils.moveFileToDirectory; import static org.sonar.core.platform.PluginInfo.jarToPluginInfo; +import static org.sonar.core.util.FileUtils.deleteQuietly; /** * Entry point to install and load plugins on server startup. It manages @@ -83,7 +83,7 @@ public class ServerPluginRepository implements PluginRepository, Startable { // following fields are available after startup private final Map<String, PluginInfo> pluginInfosByKeys = new HashMap<>(); - private final Map<String, SonarPlugin> pluginInstancesByKeys = new HashMap<>(); + private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>(); public ServerPluginRepository(Server server, ServerUpgradeStatus upgradeStatus, DefaultServerFileSystem fs, PluginLoader loader) { @@ -348,8 +348,8 @@ public class ServerPluginRepository implements PluginRepository, Startable { } @Override - public SonarPlugin getPluginInstance(String key) { - SonarPlugin plugin = pluginInstancesByKeys.get(key); + public Plugin getPluginInstance(String key) { + Plugin plugin = pluginInstancesByKeys.get(key); if (plugin == null) { throw new IllegalArgumentException(format("Plugin [%s] does not exist", key)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 8f4e7a0b87c..b1a05e4d43b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; import org.sonar.api.config.License; import org.sonar.api.config.PropertyDefinitions; @@ -287,7 +288,7 @@ public final class JRubyFacade { } public Object getComponentByClassname(String pluginKey, String className) { - SonarPlugin plugin = get(PluginRepository.class).getPluginInstance(pluginKey); + Plugin plugin = get(PluginRepository.class).getPluginInstance(pluginKey); try { Class componentClass = plugin.getClass().getClassLoader().loadClass(className); return get(componentClass); diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/I18nClassloader.java b/sonar-core/src/main/java/org/sonar/core/i18n/I18nClassloader.java index 56ed5525948..d5da2bb12a8 100644 --- a/sonar-core/src/main/java/org/sonar/core/i18n/I18nClassloader.java +++ b/sonar-core/src/main/java/org/sonar/core/i18n/I18nClassloader.java @@ -24,6 +24,7 @@ import com.google.common.collect.Lists; import java.net.URL; import java.net.URLClassLoader; import java.util.List; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; import org.sonar.core.platform.PluginInfo; import org.sonar.core.platform.PluginRepository; @@ -71,7 +72,7 @@ class I18nClassloader extends URLClassLoader { // there may be duplicated classloaders in the list. List<ClassLoader> list = Lists.newArrayList(); for (PluginInfo info : pluginRepository.getPluginInfos()) { - SonarPlugin plugin = pluginRepository.getPluginInstance(info.getKey()); + Plugin plugin = pluginRepository.getPluginInstance(info.getKey()); list.add(plugin.getClass().getClassLoader()); } list.add(I18nClassloader.class.getClassLoader()); diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java b/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java index 44b90ae7d55..d88d037fa7e 100644 --- a/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java +++ b/sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java @@ -28,7 +28,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.commons.lang.SystemUtils; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.api.utils.log.Loggers; import org.sonar.updatecenter.common.Version; @@ -46,7 +46,7 @@ import static java.util.Arrays.asList; * Plugins have their own isolated classloader, inheriting only from API classes. * Some plugins can extend a "base" plugin, sharing the same classloader. * <p/> - * This class is stateless. It does not keep pointers to classloaders and {@link org.sonar.api.SonarPlugin}. + * This class is stateless. It does not keep pointers to classloaders and {@link org.sonar.api.Plugin}. */ public class PluginLoader { @@ -67,7 +67,7 @@ public class PluginLoader { this.classloaderFactory = classloaderFactory; } - public Map<String, SonarPlugin> load(Map<String, PluginInfo> infoByKeys) { + public Map<String, Plugin> load(Map<String, PluginInfo> infoByKeys) { Collection<PluginClassLoaderDef> defs = defineClassloaders(infoByKeys); Map<PluginClassLoaderDef, ClassLoader> classloaders = classloaderFactory.create(defs); return instantiatePluginClasses(classloaders); @@ -120,15 +120,15 @@ public class PluginLoader { } /** - * Instantiates collection of {@link org.sonar.api.SonarPlugin} according to given metadata and classloaders + * Instantiates collection of {@link org.sonar.api.Plugin} according to given metadata and classloaders * * @return the instances grouped by plugin key * @throws IllegalStateException if at least one plugin can't be correctly loaded */ @VisibleForTesting - Map<String, SonarPlugin> instantiatePluginClasses(Map<PluginClassLoaderDef, ClassLoader> classloaders) { + Map<String, Plugin> instantiatePluginClasses(Map<PluginClassLoaderDef, ClassLoader> classloaders) { // instantiate plugins - Map<String, SonarPlugin> instancesByPluginKey = new HashMap<>(); + Map<String, Plugin> instancesByPluginKey = new HashMap<>(); for (Map.Entry<PluginClassLoaderDef, ClassLoader> entry : classloaders.entrySet()) { PluginClassLoaderDef def = entry.getKey(); ClassLoader classLoader = entry.getValue(); @@ -138,7 +138,7 @@ public class PluginLoader { String pluginKey = mainClassEntry.getKey(); String mainClass = mainClassEntry.getValue(); try { - instancesByPluginKey.put(pluginKey, (SonarPlugin) classLoader.loadClass(mainClass).newInstance()); + instancesByPluginKey.put(pluginKey, (Plugin) classLoader.loadClass(mainClass).newInstance()); } catch (UnsupportedClassVersionError e) { throw new IllegalStateException(String.format("The plugin [%s] does not support Java %s", pluginKey, SystemUtils.JAVA_VERSION_TRIMMED), e); @@ -151,8 +151,8 @@ public class PluginLoader { return instancesByPluginKey; } - public void unload(Collection<SonarPlugin> plugins) { - for (SonarPlugin plugin : plugins) { + public void unload(Collection<Plugin> plugins) { + for (Plugin plugin : plugins) { ClassLoader classLoader = plugin.getClass().getClassLoader(); if (classLoader instanceof Closeable && classLoader != classloaderFactory.baseClassLoader()) { try { diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PluginRepository.java b/sonar-core/src/main/java/org/sonar/core/platform/PluginRepository.java index e71e5e58bf1..9f302ac81f7 100644 --- a/sonar-core/src/main/java/org/sonar/core/platform/PluginRepository.java +++ b/sonar-core/src/main/java/org/sonar/core/platform/PluginRepository.java @@ -20,7 +20,7 @@ package org.sonar.core.platform; import java.util.Collection; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.api.batch.BatchSide; import org.sonar.api.server.ServerSide; @@ -36,9 +36,9 @@ public interface PluginRepository { PluginInfo getPluginInfo(String key); /** - * @return the instance of {@link SonarPlugin} for the given plugin key. Never return null. + * @return the instance of {@link Plugin} for the given plugin key. Never return null. */ - SonarPlugin getPluginInstance(String key); + Plugin getPluginInstance(String key); boolean hasPlugin(String key); } diff --git a/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java b/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java index e2912f3ff20..0bdef80d769 100644 --- a/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/platform/PluginLoaderTest.java @@ -30,6 +30,7 @@ import org.assertj.core.data.MapEntry; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; import org.sonar.updatecenter.common.Version; @@ -51,7 +52,7 @@ public class PluginLoaderTest { PluginClassLoaderDef def = new PluginClassLoaderDef("fake"); def.addMainClass("fake", FakePlugin.class.getName()); - Map<String, SonarPlugin> instances = loader.instantiatePluginClasses(ImmutableMap.of(def, getClass().getClassLoader())); + Map<String, Plugin> instances = loader.instantiatePluginClasses(ImmutableMap.of(def, getClass().getClassLoader())); assertThat(instances).containsOnlyKeys("fake"); assertThat(instances.get("fake")).isInstanceOf(FakePlugin.class); } @@ -168,7 +169,7 @@ public class PluginLoaderTest { public void plugin_is_not_recognised_as_system_extension_if_key_is_views_and_extends_another_plugin() throws IOException { PluginInfo foo = create52PluginInfo("foo"); PluginInfo views = create52PluginInfo("views") - .setBasePlugin("foo"); + .setBasePlugin("foo"); Collection<PluginClassLoaderDef> defs = loader.defineClassloaders(ImmutableMap.of("foo", foo, "views", views)); @@ -178,9 +179,9 @@ public class PluginLoaderTest { private PluginInfo create52PluginInfo(String pluginKey) throws IOException { File jarFile = temp.newFile(); return new PluginInfo(pluginKey) - .setJarFile(jarFile) - .setMainClass("org.foo." + pluginKey + "Plugin") - .setMinimalSqVersion(Version.create("5.2")); + .setJarFile(jarFile) + .setMainClass("org.foo." + pluginKey + "Plugin") + .setMinimalSqVersion(Version.create("5.2")); } /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java b/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java new file mode 100644 index 00000000000..ac67fe1c2bf --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java @@ -0,0 +1,139 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.api; + +import com.google.common.annotations.Beta; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Objects.requireNonNull; + +/** + * Entry-point for plugins to inject extensions into SonarQube. + * <p>The JAR manifest must declare the name of the implementation class in the property <code>Plugin-Class</code>. + * This property is automatically set by sonar-packaging-maven-plugin when building plugin.</p> + * <p>Example of implementation: + * <pre> + * package com.mycompany.sonarqube; + * public class MyPlugin implements Plugin { + * {@literal @}Override + * public void define(Context context) { + * context.addExtensions(MySensor.class, MyRules.class); + * if (context.getSonarQubeVersion().isGreaterThanOrEqual(SonarQubeVersion.V5_6)) { + * // Extension which supports only versions 5.6 and greater + * // See org.sonar.api.SonarQubeVersion for more details. + * context.addExtension(MyNewExtension.class); + * } + * } + * } + * </pre> + * </p> + * <p>Example of pom.xml:</p> + * <pre> + * <project> + * ... + * <packaging>sonar-plugin</packaging> + * + * <build> + * <plugins> + * <plugin> + * <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId> + * <artifactId>sonar-packaging-maven-plugin</artifactId> + * <extensions>true</extensions> + * <configuration> + * <pluginClass>com.mycompany.sonarqube.MyPlugin</pluginClass> + * </configuration> + * </plugin> + * </plugins> + * </build> + * </project> + * </pre> + * + * @since 5.5 + */ +@Beta +public interface Plugin { + + class Context { + private final SonarQubeVersion version; + private final List extensions = new ArrayList(); + + public Context(SonarQubeVersion version) { + this.version = version; + } + + public SonarQubeVersion getSonarQubeVersion() { + return version; + } + + /** + * Add an extension as : + * <ul> + * <li>a Class that is annotated with {@link org.sonar.api.batch.BatchSide} or {@link org.sonar.api.server.ServerSide}. + * The extension will be instantiated once. Its dependencies are injected through constructor parameters.</li> + * <li>an instance that is annotated with {@link org.sonar.api.batch.BatchSide} or {@link org.sonar.api.server.ServerSide}</li> + * </ul> + * Only a single component can be registered for a class. It's not allowed for example to register: + * <ul> + * <li>two MyExtension.class</li> + * <li>MyExtension.class and new MyExtension()</li> + * </ul> + */ + public Context addExtension(Object extension) { + requireNonNull(extension); + this.extensions.add(extension); + return this; + } + + /** + * @see #addExtension(Object) + */ + public Context addExtensions(Collection extensions) { + this.extensions.addAll(extensions); + return this; + } + + /** + * @see #addExtension(Object) + */ + public Context addExtensions(Object first, Object second, Object... others) { + addExtension(first); + addExtension(second); + addExtensions(asList(others)); + return this; + } + + public List getExtensions() { + return extensions; + } + } + + /** + * This method is executed at runtime when: + * <ul> + * <li>Web Server starts</li> + * <li>Compute Engine starts</li> + * <li>Scanner starts</li> + * </ul> + */ + void define(Context context); +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/SonarPlugin.java b/sonar-plugin-api/src/main/java/org/sonar/api/SonarPlugin.java index 823f1517b60..d9d45225532 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/SonarPlugin.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/SonarPlugin.java @@ -29,7 +29,7 @@ import java.util.List; * * @since 2.8 */ -public abstract class SonarPlugin { +public abstract class SonarPlugin implements Plugin { /** * Classes of the implemented extensions. @@ -44,4 +44,8 @@ public abstract class SonarPlugin { return getClass().getSimpleName(); } + @Override + public void define(Context context) { + context.addExtensions(getExtensions()); + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java index d1544367704..260df6f9722 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java @@ -56,7 +56,7 @@ import static java.lang.String.format; * the ws is fully implemented in Java and does not require any Ruby on Rails code. * <p/> * <p/> - * The classes implementing this extension point must be declared in {@link org.sonar.api.SonarPlugin#getExtensions()}. + * The classes implementing this extension point must be declared by {@link org.sonar.api.Plugin}. * <p/> * <h3>How to use</h3> * <pre> diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java new file mode 100644 index 00000000000..204a430dde3 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/PluginTest.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.api; + +import java.util.Arrays; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.SonarQubeVersion.V5_5; + +public class PluginTest { + + @Test + public void test_context() { + Plugin.Context context = new Plugin.Context(new SonarQubeVersion(V5_5)); + + assertThat(context.getSonarQubeVersion().get()).isEqualTo(V5_5); + assertThat(context.getExtensions()).isEmpty(); + + context.addExtension("foo"); + assertThat(context.getExtensions()).containsOnly("foo"); + + context.addExtensions(Arrays.asList("bar", "baz")); + assertThat(context.getExtensions()).containsOnly("foo", "bar", "baz"); + + context.addExtensions("one", "two", "three", "four"); + assertThat(context.getExtensions()).containsOnly("foo", "bar", "baz", "one", "two", "three", "four"); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index 0a2ec661d73..aa107887ea4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -43,7 +43,6 @@ import org.sonar.batch.task.Tasks; import org.sonar.core.component.DefaultResourceTypes; import org.sonar.core.config.CorePropertyDefinitions; import org.sonar.core.issue.tracking.Tracker; -import org.sonar.core.platform.SonarQubeVersionProvider; public class BatchComponents { private BatchComponents() { @@ -52,7 +51,6 @@ public class BatchComponents { public static Collection<Object> all(AnalysisMode analysisMode) { List<Object> components = Lists.newArrayList( - new SonarQubeVersionProvider(), DefaultResourceTypes.get(), // Tasks diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java index 1c1ee8e1fc4..b1dd6aa5843 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java @@ -31,7 +31,7 @@ import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.CharUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.SonarPlugin; +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; @@ -94,7 +94,7 @@ public class BatchPluginInstaller implements PluginInstaller { * @see org.sonar.batch.mediumtest.BatchMediumTester */ @Override - public Map<String, SonarPlugin> installLocals() { + public Map<String, Plugin> installLocals() { return Collections.emptyMap(); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java index 4d0d0c46c2d..6a111cf23c0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java @@ -19,15 +19,14 @@ */ package org.sonar.batch.bootstrap; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; - import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import java.util.Collection; import java.util.Map; import org.picocontainer.Startable; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.core.platform.PluginInfo; import org.sonar.core.platform.PluginLoader; import org.sonar.core.platform.PluginRepository; @@ -41,7 +40,7 @@ public class BatchPluginRepository implements PluginRepository, Startable { private final PluginInstaller installer; private final PluginLoader loader; - private Map<String, SonarPlugin> pluginInstancesByKeys; + private Map<String, Plugin> pluginInstancesByKeys; private Map<String, PluginInfo> infosByKeys; public BatchPluginRepository(PluginInstaller installer, PluginLoader loader) { @@ -55,7 +54,7 @@ public class BatchPluginRepository implements PluginRepository, Startable { pluginInstancesByKeys = Maps.newHashMap(loader.load(infosByKeys)); // this part is only used by tests - for (Map.Entry<String, SonarPlugin> entry : installer.installLocals().entrySet()) { + for (Map.Entry<String, Plugin> entry : installer.installLocals().entrySet()) { String pluginKey = entry.getKey(); infosByKeys.put(pluginKey, new PluginInfo(pluginKey)); pluginInstancesByKeys.put(pluginKey, entry.getValue()); @@ -97,8 +96,8 @@ public class BatchPluginRepository implements PluginRepository, Startable { } @Override - public SonarPlugin getPluginInstance(String key) { - SonarPlugin instance = pluginInstancesByKeys.get(key); + public Plugin getPluginInstance(String key) { + Plugin instance = pluginInstancesByKeys.get(key); Preconditions.checkState(instance != null, String.format("Plugin [%s] does not exist", key)); return instance; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java index ff610139032..dfab90ea7e8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java @@ -22,7 +22,9 @@ package org.sonar.batch.bootstrap; import java.util.List; import javax.annotation.Nullable; import org.sonar.api.ExtensionProvider; +import org.sonar.api.Plugin; import org.sonar.api.SonarPlugin; +import org.sonar.api.SonarQubeVersion; import org.sonar.api.batch.AnalysisMode; import org.sonar.core.platform.ComponentContainer; import org.sonar.core.platform.PluginInfo; @@ -30,10 +32,12 @@ import org.sonar.core.platform.PluginRepository; public class ExtensionInstaller { + private final SonarQubeVersion sonarQubeVersion; private final PluginRepository pluginRepository; private final AnalysisMode analysisMode; - public ExtensionInstaller(PluginRepository pluginRepository, AnalysisMode analysisMode) { + public ExtensionInstaller(SonarQubeVersion sonarQubeVersion, PluginRepository pluginRepository, AnalysisMode analysisMode) { + this.sonarQubeVersion = sonarQubeVersion; this.pluginRepository = pluginRepository; this.analysisMode = analysisMode; } @@ -47,8 +51,10 @@ public class ExtensionInstaller { // plugin extensions for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { - SonarPlugin plugin = pluginRepository.getPluginInstance(pluginInfo.getKey()); - for (Object extension : plugin.getExtensions()) { + Plugin plugin = pluginRepository.getPluginInstance(pluginInfo.getKey()); + Plugin.Context context = new Plugin.Context(sonarQubeVersion); + plugin.define(context); + for (Object extension : context.getExtensions()) { doInstall(container, matcher, pluginInfo, extension); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java index 8ac69fd6258..ea6e967da42 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java @@ -21,7 +21,7 @@ package org.sonar.batch.bootstrap; import java.util.List; import java.util.Map; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.api.utils.System2; import org.sonar.api.utils.UriReader; import org.sonar.batch.cache.GlobalPersistentCacheProvider; @@ -39,6 +39,7 @@ import org.sonar.core.platform.PluginClassloaderFactory; import org.sonar.core.platform.PluginInfo; import org.sonar.core.platform.PluginLoader; import org.sonar.core.platform.PluginRepository; +import org.sonar.core.platform.SonarQubeVersionProvider; import org.sonar.core.util.DefaultHttpDownloader; import org.sonar.core.util.UuidFactoryImpl; @@ -89,6 +90,7 @@ public class GlobalContainer extends ComponentContainer { BatchPluginPredicate.class, ExtensionInstaller.class, + new SonarQubeVersionProvider(), CachesManager.class, GlobalSettings.class, new BatchWsClientProvider(), @@ -113,7 +115,7 @@ public class GlobalContainer extends ComponentContainer { private void installPlugins() { PluginRepository pluginRepository = getComponentByType(PluginRepository.class); for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) { - SonarPlugin instance = pluginRepository.getPluginInstance(pluginInfo.getKey()); + Plugin instance = pluginRepository.getPluginInstance(pluginInfo.getKey()); addExtension(pluginInfo, instance); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/PluginInstaller.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/PluginInstaller.java index 64f923bd9a5..4525027dbaa 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/PluginInstaller.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/bootstrap/PluginInstaller.java @@ -20,7 +20,7 @@ package org.sonar.batch.bootstrap; import java.util.Map; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.api.batch.BatchSide; import org.sonar.core.platform.PluginInfo; @@ -38,5 +38,5 @@ public interface PluginInstaller { * Used only by tests. * @see org.sonar.batch.mediumtest.BatchMediumTester */ - Map<String, SonarPlugin> installLocals(); + Map<String, Plugin> installLocals(); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/mediumtest/FakePluginInstaller.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/mediumtest/FakePluginInstaller.java index 71205c7b08d..561d7271768 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/mediumtest/FakePluginInstaller.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/mediumtest/FakePluginInstaller.java @@ -22,7 +22,7 @@ package org.sonar.batch.mediumtest; import java.io.File; import java.util.HashMap; import java.util.Map; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.batch.bootstrap.PluginInstaller; import org.sonar.core.platform.PluginInfo; @@ -30,14 +30,14 @@ public class FakePluginInstaller implements PluginInstaller { public static final String MEDIUM_TEST_ENABLED = "sonar.mediumTest.enabled"; private final Map<String, PluginInfo> infosByKeys = new HashMap<>(); - private final Map<String, SonarPlugin> instancesByKeys = new HashMap<>(); + private final Map<String, Plugin> instancesByKeys = new HashMap<>(); public FakePluginInstaller add(String pluginKey, File jarFile) { infosByKeys.put(pluginKey, PluginInfo.create(jarFile)); return this; } - public FakePluginInstaller add(String pluginKey, SonarPlugin instance) { + public FakePluginInstaller add(String pluginKey, Plugin instance) { instancesByKeys.put(pluginKey, instance); return this; } @@ -48,7 +48,7 @@ public class FakePluginInstaller implements PluginInstaller { } @Override - public Map<String, SonarPlugin> installLocals() { + public Map<String, Plugin> installLocals() { return instancesByKeys; } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java index 0d0fc068dfe..58233e9b2be 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java @@ -21,7 +21,7 @@ package org.sonar.batch.bootstrap; import com.google.common.collect.ImmutableMap; import org.junit.Test; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.core.platform.PluginInfo; import org.sonar.core.platform.PluginLoader; @@ -42,7 +42,7 @@ public class BatchPluginRepositoryTest { public void install_and_load_plugins() { PluginInfo info = new PluginInfo("squid"); ImmutableMap<String, PluginInfo> infos = ImmutableMap.of("squid", info); - SonarPlugin instance = mock(SonarPlugin.class); + Plugin instance = mock(Plugin.class); when(loader.load(infos)).thenReturn(ImmutableMap.of("squid", instance)); when(installer.installRemotes()).thenReturn(infos); @@ -53,7 +53,7 @@ public class BatchPluginRepositoryTest { assertThat(underTest.getPluginInstance("squid")).isSameAs(instance); underTest.stop(); - verify(loader).unload(anyCollectionOf(SonarPlugin.class)); + verify(loader).unload(anyCollectionOf(Plugin.class)); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java index c368fb146b6..6b09a5d24f7 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.sonar.api.BatchExtension; import org.sonar.api.ExtensionProvider; import org.sonar.api.SonarPlugin; +import org.sonar.api.SonarQubeVersion; import org.sonar.api.batch.AnalysisMode; import org.sonar.core.platform.ComponentContainer; import org.sonar.core.platform.PluginInfo; @@ -59,7 +60,7 @@ public class ExtensionInstallerTest { when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(Foo.class, Bar.class)); ComponentContainer container = new ComponentContainer(); - ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class)); + ExtensionInstaller installer = new ExtensionInstaller(mock(SonarQubeVersion.class), pluginRepository, mock(AnalysisMode.class)); installer.install(container, new FooMatcher()); assertThat(container.getComponentByType(Foo.class)).isNotNull(); @@ -71,7 +72,7 @@ public class ExtensionInstallerTest { when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo"))); when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooProvider(), new BarProvider())); ComponentContainer container = new ComponentContainer(); - ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class)); + ExtensionInstaller installer = new ExtensionInstaller(mock(SonarQubeVersion.class), pluginRepository, mock(AnalysisMode.class)); installer.install(container, new FooMatcher()); @@ -84,7 +85,7 @@ public class ExtensionInstallerTest { when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo"))); when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooBarProvider())); ComponentContainer container = new ComponentContainer(); - ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class)); + ExtensionInstaller installer = new ExtensionInstaller(mock(SonarQubeVersion.class), pluginRepository, mock(AnalysisMode.class)); installer.install(container, new TrueMatcher()); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java index 619977ce156..f405eea8785 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -43,7 +43,8 @@ import org.sonar.batch.rule.RulesLoader; import org.sonar.scanner.protocol.input.GlobalRepositories; import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; import com.google.common.base.Function; - +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -58,20 +59,35 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; - +import javax.annotation.Nullable; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.mutable.MutableBoolean; import org.sonar.api.CoreProperties; -import org.sonar.api.SonarPlugin; +import org.sonar.api.Plugin; import org.sonar.api.batch.debt.internal.DefaultDebtModel; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.server.rule.RulesDefinition.Repository; +import org.sonar.api.utils.DateUtils; import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonar.batch.bootstrapper.IssueListener; import org.sonar.batch.bootstrapper.LogOutput; import org.sonar.batch.issue.tracking.ServerLineHashesLoader; import org.sonar.batch.report.ReportPublisher; +import org.sonar.batch.repository.FileData; import org.sonar.batch.repository.GlobalRepositoriesLoader; +import org.sonar.batch.repository.ProjectRepositories; import org.sonar.batch.repository.ProjectRepositoriesLoader; +import org.sonar.batch.repository.QualityProfileLoader; import org.sonar.batch.repository.ServerIssuesLoader; +import org.sonar.batch.rule.ActiveRulesLoader; +import org.sonar.batch.rule.LoadedActiveRule; +import org.sonar.batch.rule.RulesLoader; +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; +import org.sonarqube.ws.Rules.ListResponse.Rule; /** * Main utility class for writing batch medium tests. @@ -151,7 +167,7 @@ public class BatchMediumTester { return this; } - public BatchMediumTesterBuilder registerPlugin(String pluginKey, SonarPlugin instance) { + public BatchMediumTesterBuilder registerPlugin(String pluginKey, Plugin instance) { pluginInstaller.add(pluginKey, instance); return this; } @@ -234,7 +250,7 @@ public class BatchMediumTester { r.setTemplateRuleKey(templateRuleKey); r.setLanguage(languag); r.setSeverity(severity); - + activeRules.addActiveRule(r); return this; } |