From: Julien HENRY Date: Thu, 3 Aug 2017 11:15:32 +0000 (+0200) Subject: SONAR-9672 Store the link between a rule and the plugin declaring it X-Git-Tag: 6.6-RC1~440 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e992c54eda36632088c2da41174f703a49282c35;p=sonarqube.git SONAR-9672 Store the link between a rule and the plugin declaring it --- diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index 2a775ea8298..130b892622b 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -115,7 +115,6 @@ public class ComputeEngineContainerImplTest { underTest .start(new Props(properties)); - MutablePicoContainer picoContainer = underTest.getComponentContainer().getPicoContainer(); assertThat(picoContainer.getComponentAdapters()) .hasSize( diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index 8cbd7afb001..aa131c20e11 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -151,6 +151,7 @@ CREATE TABLE "RULE_REPOSITORIES" ( CREATE TABLE "RULES" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "PLUGIN_KEY" VARCHAR(200), "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL, "PLUGIN_NAME" VARCHAR(255) NOT NULL, "DESCRIPTION" VARCHAR(16777215), diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java index 31c60553853..13c687305c1 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java @@ -57,6 +57,8 @@ public class RuleDefinitionDto { private RuleKey key; + private String pluginKey; + private long createdAt; private long updatedAt; @@ -293,6 +295,15 @@ public class RuleDefinitionDto { return this; } + public String getPluginKey() { + return pluginKey; + } + + public RuleDefinitionDto setPluginKey(String pluginKey) { + this.pluginKey = pluginKey; + return this; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof RuleDto)) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java index 64d22aa8dac..e5861debe31 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java @@ -89,6 +89,15 @@ public class RuleDto { return this; } + public String getPluginKey() { + return definition.getPluginKey(); + } + + public RuleDto setPluginKey(String s) { + definition.setPluginKey(s); + return this; + } + public String getDescription() { return definition.getDescription(); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml index 5178e00e2da..d1a161504b4 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml @@ -22,6 +22,7 @@ r.gap_description as "gapDescription", r.system_tags as "systemTagsField", r.rule_type as "type", + r.plugin_key as "pluginKey", @@ -282,6 +283,7 @@ insert into rules ( + plugin_key, plugin_rule_key, plugin_name, description, @@ -303,6 +305,7 @@ updated_at ) values ( + #{pluginKey,jdbcType=VARCHAR}, #{ruleKey,jdbcType=VARCHAR}, #{repositoryKey,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, @@ -327,6 +330,7 @@ update rules set + plugin_key=#{pluginKey,jdbcType=VARCHAR}, plugin_rule_key=#{ruleKey,jdbcType=VARCHAR}, plugin_name=#{repositoryKey,jdbcType=VARCHAR}, description=#{description,jdbcType=VARCHAR}, diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRules.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRules.java new file mode 100644 index 00000000000..4fa05dc1927 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRules.java @@ -0,0 +1,46 @@ +/* + * 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.server.platform.db.migration.version.v66; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; + +public class AddPluginKeyToRules extends DdlChange { + + public AddPluginKeyToRules(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute(new AddColumnsBuilder(getDialect(), "rules") + .addColumn(newVarcharColumnDefBuilder() + .setColumnName("plugin_key") + .setLimit(200) + .setIsNullable(true) + .build()) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66.java index c8df2d86685..3ac1c79b7fa 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66.java @@ -31,6 +31,6 @@ public class DbVersion66 implements DbVersion { .add(1801, "Create table CE task characteristics", CreateTableCeTaskCharacteristics.class) .add(1802, "Delete leak settings on views", DeleteLeakSettingsOnViews.class) .add(1803, "Fix empty USERS.EXTERNAL_IDENTITY and USERS.EXTERNAL_IDENTITY_PROVIDER", FixEmptyIdentityProviderInUsers.class) - ; + .add(1804, "Add rules.plugin_key", AddPluginKeyToRules.class); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRulesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRulesTest.java new file mode 100644 index 00000000000..d4a0cce4ee4 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRulesTest.java @@ -0,0 +1,53 @@ +/* + * 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.server.platform.db.migration.version.v66; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; + +public class AddPluginKeyToRulesTest { + @Rule + public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddPluginKeyToRulesTest.class, "rules_6_5.sql"); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private AddPluginKeyToRules underTest = new AddPluginKeyToRules(dbTester.database()); + + @Test + public void column_is_added_to_table() throws SQLException { + underTest.execute(); + + dbTester.assertColumnDefinition("rules", "plugin_key", java.sql.Types.VARCHAR, 200, true); + } + + @Test + public void migration_is_not_reentrant() throws SQLException { + underTest.execute(); + + expectedException.expect(IllegalStateException.class); + + underTest.execute(); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66Test.java index 4a97a611e52..591df1a3dd4 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/DbVersion66Test.java @@ -17,6 +17,7 @@ * 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.server.platform.db.migration.version.v66; import org.junit.Test; @@ -34,6 +35,6 @@ public class DbVersion66Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 4); + verifyMigrationCount(underTest, 5); } } diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRulesTest/rules_6_5.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRulesTest/rules_6_5.sql new file mode 100644 index 00000000000..375442e561b --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v66/AddPluginKeyToRulesTest/rules_6_5.sql @@ -0,0 +1,23 @@ +CREATE TABLE "RULES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL, + "PLUGIN_NAME" VARCHAR(255) NOT NULL, + "DESCRIPTION" VARCHAR(16777215), + "DESCRIPTION_FORMAT" VARCHAR(20), + "PRIORITY" INTEGER, + "IS_TEMPLATE" BOOLEAN DEFAULT FALSE, + "TEMPLATE_ID" INTEGER, + "PLUGIN_CONFIG_KEY" VARCHAR(200), + "NAME" VARCHAR(200), + "STATUS" VARCHAR(40), + "LANGUAGE" VARCHAR(20), + "DEF_REMEDIATION_FUNCTION" VARCHAR(20), + "DEF_REMEDIATION_GAP_MULT" VARCHAR(20), + "DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20), + "GAP_DESCRIPTION" VARCHAR(4000), + "SYSTEM_TAGS" VARCHAR(4000), + "RULE_TYPE" TINYINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT +); +CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY"); \ No newline at end of file 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 07ef2553697..4edc0f7768c 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 @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.apache.commons.io.FileUtils; import org.picocontainer.Startable; @@ -93,6 +94,7 @@ public class ServerPluginRepository implements PluginRepository, Startable { // following fields are available after startup private final Map pluginInfosByKeys = new HashMap<>(); private final Map pluginInstancesByKeys = new HashMap<>(); + private final Map keysByClassLoader = new HashMap<>(); public ServerPluginRepository(SonarRuntime runtime, ServerUpgradeStatus upgradeStatus, ServerFileSystem fs, PluginLoader loader) { @@ -124,9 +126,18 @@ public class ServerPluginRepository implements PluginRepository, Startable { loader.unload(pluginInstancesByKeys.values()); pluginInstancesByKeys.clear(); pluginInfosByKeys.clear(); + keysByClassLoader.clear(); started.set(true); } + /** + * Return the key of the plugin the extension (in the sense of {@link Plugin.Context#addExtension(Object)} is coming from. + */ + @CheckForNull + public String getPluginKey(Object extension) { + return keysByClassLoader.get(extension.getClass().getClassLoader()); + } + /** * Load the plugins that are located in extensions/plugins. Blacklisted plugins are * deleted. @@ -286,6 +297,10 @@ public class ServerPluginRepository implements PluginRepository, Startable { private void loadInstances() { pluginInstancesByKeys.putAll(loader.load(pluginInfosByKeys)); + + for (Map.Entry e : pluginInstancesByKeys.entrySet()) { + keysByClassLoader.put(e.getValue().getClass().getClassLoader(), e.getKey()); + } } /** diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java b/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java index b4511c6d5ad..2e09fec93a9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoader.java @@ -29,11 +29,11 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rules.RuleParam; import org.sonar.api.rules.RuleRepository; -import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.server.ServerSide; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.rule.RuleParamType; @@ -46,6 +46,7 @@ import org.sonar.server.debt.DebtModelPluginRepository; import org.sonar.server.debt.DebtModelXMLExporter; import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt; import org.sonar.server.debt.DebtRulesXMLImporter; +import org.sonar.server.plugins.ServerPluginRepository; import static com.google.common.collect.Lists.newArrayList; @@ -63,9 +64,12 @@ public class DeprecatedRulesDefinitionLoader { private final DebtModelPluginRepository languageModelFinder; private final DebtRulesXMLImporter importer; + private final ServerPluginRepository serverPluginRepository; - public DeprecatedRulesDefinitionLoader(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer, RuleRepository[] repositories) { + public DeprecatedRulesDefinitionLoader(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer, + ServerPluginRepository serverPluginRepository, RuleRepository[] repositories) { this.i18n = i18n; + this.serverPluginRepository = serverPluginRepository; this.repositories = repositories; this.languageModelFinder = languageModelFinder; this.importer = importer; @@ -74,8 +78,9 @@ public class DeprecatedRulesDefinitionLoader { /** * Used when no deprecated repositories */ - public DeprecatedRulesDefinitionLoader(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer) { - this(i18n, languageModelFinder, importer, new RuleRepository[0]); + public DeprecatedRulesDefinitionLoader(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer, + ServerPluginRepository serverPluginRepository) { + this(i18n, languageModelFinder, importer, serverPluginRepository, new RuleRepository[0]); } void complete(RulesDefinition.Context context) { @@ -83,6 +88,7 @@ public class DeprecatedRulesDefinitionLoader { List ruleDebts = loadRuleDebtList(); for (RuleRepository repository : repositories) { + context.setCurrentPluginKey(serverPluginRepository.getPluginKey(repository)); // RuleRepository API does not handle difference between new and extended repositories, RulesDefinition.NewRepository newRepository; if (context.repository(repository.getKey()) == null) { @@ -119,8 +125,7 @@ public class DeprecatedRulesDefinitionLoader { ruleDebt.coefficient(), ruleDebt.offset(), newRule.debtRemediationFunctions(), - repoKey, ruleKey - )); + repoKey, ruleKey)); } } @@ -159,8 +164,7 @@ public class DeprecatedRulesDefinitionLoader { private String paramDescription(String repositoryKey, String ruleKey, RuleParam param) { String desc = StringUtils.defaultIfEmpty( i18n.getParamDescription(repositoryKey, ruleKey, param.getKey()), - param.getDescription() - ); + param.getDescription()); return StringUtils.defaultIfBlank(desc, null); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index a3750b27789..70e2eacbece 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -214,6 +214,7 @@ public class RegisterRules implements Startable { private RuleDefinitionDto createRuleDto(RulesDefinition.Rule ruleDef, DbSession session) { RuleDefinitionDto ruleDto = new RuleDefinitionDto() .setRuleKey(RuleKey.of(ruleDef.repository().key(), ruleDef.key())) + .setPluginKey(ruleDef.pluginKey()) .setIsTemplate(ruleDef.template()) .setConfigKey(ruleDef.internalKey()) .setLanguage(ruleDef.repository().language()) @@ -246,6 +247,10 @@ public class RegisterRules implements Startable { if (mergeDescription(def, dto)) { changed = true; } + if (!StringUtils.equals(dto.getPluginKey(), def.pluginKey())) { + dto.setPluginKey(def.pluginKey()); + changed = true; + } if (!StringUtils.equals(dto.getConfigKey(), def.internalKey())) { dto.setConfigKey(def.internalKey()); changed = true; @@ -459,6 +464,10 @@ public class RegisterRules implements Startable { customRule.setConfigKey(templateRule.getConfigKey()); changed = true; } + if (!StringUtils.equals(customRule.getPluginKey(), templateRule.getPluginKey())) { + customRule.setPluginKey(templateRule.getPluginKey()); + changed = true; + } if (!StringUtils.equals(customRule.getDefRemediationFunction(), templateRule.getDefRemediationFunction())) { customRule.setDefRemediationFunction(templateRule.getDefRemediationFunction()); changed = true; diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java index 941cc2835bf..f9ad9c6ce6a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java @@ -158,6 +158,7 @@ public class RuleCreator { private RuleKey createCustomRule(RuleKey ruleKey, NewCustomRule newRule, RuleDto templateRuleDto, DbSession dbSession) { RuleDefinitionDto ruleDefinition = new RuleDefinitionDto() .setRuleKey(ruleKey) + .setPluginKey(templateRuleDto.getPluginKey()) .setTemplateId(templateRuleDto.getId()) .setConfigKey(templateRuleDto.getConfigKey()) .setName(newRule.name()) diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java index 9652e4fc910..fdd5d565cc4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleDefinitionsLoader.java @@ -20,6 +20,7 @@ package org.sonar.server.rule; import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.server.plugins.ServerPluginRepository; /** * Loads all instances of {@link RulesDefinition}. Used during server startup @@ -30,11 +31,13 @@ public class RuleDefinitionsLoader { private final DeprecatedRulesDefinitionLoader deprecatedDefConverter; private final CommonRuleDefinitions coreCommonDefs; private final RulesDefinition[] pluginDefs; + private final ServerPluginRepository serverPluginRepository; public RuleDefinitionsLoader(DeprecatedRulesDefinitionLoader deprecatedDefConverter, - CommonRuleDefinitions coreCommonDefs, RulesDefinition[] pluginDefs) { + CommonRuleDefinitions coreCommonDefs, ServerPluginRepository serverPluginRepository, RulesDefinition[] pluginDefs) { this.deprecatedDefConverter = deprecatedDefConverter; this.coreCommonDefs = coreCommonDefs; + this.serverPluginRepository = serverPluginRepository; this.pluginDefs = pluginDefs; } @@ -42,16 +45,18 @@ public class RuleDefinitionsLoader { * Used when no definitions at all. */ public RuleDefinitionsLoader(DeprecatedRulesDefinitionLoader converter, - CommonRuleDefinitions coreCommonDefs) { - this(converter, coreCommonDefs, new RulesDefinition[0]); + CommonRuleDefinitions coreCommonDefs, ServerPluginRepository serverPluginRepository) { + this(converter, coreCommonDefs, serverPluginRepository, new RulesDefinition[0]); } public RulesDefinition.Context load() { RulesDefinition.Context context = new RulesDefinition.Context(); for (RulesDefinition pluginDefinition : pluginDefs) { + context.setCurrentPluginKey(serverPluginRepository.getPluginKey(pluginDefinition)); pluginDefinition.define(context); } deprecatedDefConverter.complete(context); + context.setCurrentPluginKey(null); coreCommonDefs.define(context); return context; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java index 6ec467354dd..7ec4f6e11bc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java @@ -39,6 +39,7 @@ import org.sonar.core.i18n.RuleI18nManager; import org.sonar.server.debt.DebtModelPluginRepository; import org.sonar.server.debt.DebtModelXMLExporter; import org.sonar.server.debt.DebtRulesXMLImporter; +import org.sonar.server.plugins.ServerPluginRepository; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; @@ -60,6 +61,9 @@ public class DeprecatedRulesDefinitionLoaderTest { @Mock DebtRulesXMLImporter importer; + @Mock + ServerPluginRepository pluginRepository; + static class CheckstyleRules extends RuleRepository { public CheckstyleRules() { super("checkstyle", "java"); @@ -96,7 +100,9 @@ public class DeprecatedRulesDefinitionLoaderTest { @Test public void wrap_deprecated_rule_repositories() { RulesDefinition.Context context = new RulesDefinition.Context(); - new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, new RuleRepository[] {new CheckstyleRules()}).complete(context); + CheckstyleRules checkstyleRules = new CheckstyleRules(); + when(pluginRepository.getPluginKey(checkstyleRules)).thenReturn("unittest"); + new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {checkstyleRules}).complete(context); assertThat(context.repositories()).hasSize(1); RulesDefinition.Repository checkstyle = context.repository("checkstyle"); @@ -108,6 +114,7 @@ public class DeprecatedRulesDefinitionLoaderTest { RulesDefinition.Rule rule = checkstyle.rule("ConstantName"); assertThat(rule).isNotNull(); assertThat(rule.key()).isEqualTo("ConstantName"); + assertThat(rule.pluginKey()).isEqualTo("unittest"); assertThat(rule.name()).isEqualTo("Constant Name"); assertThat(rule.htmlDescription()).isEqualTo("Checks that constant names conform to the specified format"); assertThat(rule.severity()).isEqualTo(Severity.BLOCKER); @@ -128,7 +135,7 @@ public class DeprecatedRulesDefinitionLoaderTest { RulesDefinition.Context context = new RulesDefinition.Context(); // no more RuleRepository ! - new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer); + new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository); assertThat(context.repositories()).isEmpty(); } @@ -140,7 +147,7 @@ public class DeprecatedRulesDefinitionLoaderTest { when(i18n.getDescription("checkstyle", "ConstantName")).thenReturn("Checks that constant names conform to the specified format"); when(i18n.getParamDescription("checkstyle", "ConstantName", "format")).thenReturn("Regular expression"); - new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, new RuleRepository[] {new UseBundles()}).complete(context); + new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {new UseBundles()}).complete(context); RulesDefinition.Repository checkstyle = context.repository("checkstyle"); RulesDefinition.Rule rule = checkstyle.rule("ConstantName"); @@ -162,15 +169,14 @@ public class DeprecatedRulesDefinitionLoaderTest { .setRuleKey(RuleKey.of("checkstyle", "ConstantName")) .setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) .setCoefficient("1d") - .setOffset("10min") - ); + .setOffset("10min")); Reader javaModelReader = mock(Reader.class); when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); when(debtModelRepository.getContributingPluginList()).thenReturn(newArrayList("java")); when(importer.importXML(eq(javaModelReader), any(ValidationMessages.class))).thenReturn(ruleDebts); - new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, new RuleRepository[] {new CheckstyleRules()}).complete(context); + new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {new CheckstyleRules()}).complete(context); assertThat(context.repositories()).hasSize(1); RulesDefinition.Repository checkstyle = context.repository("checkstyle"); @@ -192,8 +198,7 @@ public class DeprecatedRulesDefinitionLoaderTest { new DebtModelXMLExporter.RuleDebt() .setRuleKey(RuleKey.of("checkstyle", "ConstantName")) .setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) - .setCoefficient("1d") - ); + .setCoefficient("1d")); Reader javaModelReader = mock(Reader.class); when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); @@ -201,7 +206,7 @@ public class DeprecatedRulesDefinitionLoaderTest { when(importer.importXML(eq(javaModelReader), any(ValidationMessages.class))).thenReturn(ruleDebts); try { - new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, new RuleRepository[] {new CheckstyleRules()}).complete(context); + new DeprecatedRulesDefinitionLoader(i18n, debtModelRepository, importer, pluginRepository, new RuleRepository[] {new CheckstyleRules()}).complete(context); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java index 50db488c3a3..04274707801 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java @@ -47,6 +47,7 @@ import org.sonar.server.es.EsTester; import org.sonar.server.es.SearchOptions; import org.sonar.server.organization.OrganizationFlags; import org.sonar.server.organization.TestOrganizationFlags; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.qualityprofile.RuleActivator; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.rule.index.RuleIndex; @@ -58,6 +59,7 @@ import static com.google.common.collect.Sets.newHashSet; import static java.util.Collections.singletonList; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -67,6 +69,7 @@ import static org.sonar.api.rule.Severity.INFO; public class RegisterRulesTest { + private static final String FAKE_PLUGIN_KEY = "unittest"; private static final Date DATE1 = DateUtils.parseDateTime("2014-01-01T19:10:03+0100"); private static final Date DATE2 = DateUtils.parseDateTime("2014-02-01T12:10:03+0100"); private static final Date DATE3 = DateUtils.parseDateTime("2014-03-01T12:10:03+0100"); @@ -122,6 +125,7 @@ public class RegisterRulesTest { assertThat(rule1.getDefRemediationGapMultiplier()).isEqualTo("5d"); assertThat(rule1.getDefRemediationBaseEffort()).isEqualTo("10h"); assertThat(rule1.getType()).isEqualTo(RuleType.CODE_SMELL.getDbConstant()); + assertThat(rule1.getPluginKey()).isEqualTo(FAKE_PLUGIN_KEY); List params = dbClient.ruleDao().selectRuleParamsByRuleKey(dbTester.getSession(), RULE_KEY1); assertThat(params).hasSize(2); @@ -481,7 +485,10 @@ public class RegisterRulesTest { } private void execute(RulesDefinition... defs) { - RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), mock(CommonRuleDefinitionsImpl.class), defs); + ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class); + when(pluginRepository.getPluginKey(any(RulesDefinition.class))).thenReturn(FAKE_PLUGIN_KEY); + RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), mock(CommonRuleDefinitionsImpl.class), pluginRepository, + defs); Languages languages = mock(Languages.class); when(languages.get("java")).thenReturn(mock(Language.class)); reset(webServerRuleFinder); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorTest.java index 9dbda10be8d..3e0c0cb7cdb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorTest.java @@ -91,6 +91,7 @@ public class RuleCreatorTest { RuleDto rule = dbTester.getDbClient().ruleDao().selectOrFailByKey(dbSession, dbTester.getDefaultOrganization(), customRuleKey); assertThat(rule).isNotNull(); assertThat(rule.getKey()).isEqualTo(RuleKey.of("java", "CUSTOM_RULE")); + assertThat(rule.getPluginKey()).isEqualTo("sonarjava"); assertThat(rule.getTemplateId()).isEqualTo(templateRule.getId()); assertThat(rule.getName()).isEqualTo("My custom"); assertThat(rule.getDescription()).isEqualTo("Some description"); @@ -461,6 +462,7 @@ public class RuleCreatorTest { RuleDto templateRule = RuleTesting.newDto(RuleKey.of("java", "S001"), dbTester.getDefaultOrganization()) .setIsTemplate(true) .setLanguage("java") + .setPluginKey("sonarjava") .setConfigKey("S001") .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name()) .setDefRemediationGapMultiplier("1h") @@ -489,7 +491,8 @@ public class RuleCreatorTest { .setCreatedAt(new Date().getTime()) .setUpdatedAt(new Date().getTime()); dbTester.rules().insert(templateRule); - dbTester.rules().insertRuleParam(templateRule, param -> param.setName("myIntegers").setType("INTEGER,multiple=true,values=1;2;3").setDescription("My Integers").setDefaultValue("1")); + dbTester.rules().insertRuleParam(templateRule, + param -> param.setName("myIntegers").setType("INTEGER,multiple=true,values=1;2;3").setDescription("My Integers").setDefaultValue("1")); ruleIndexer.commitAndIndex(dbTester.getSession(), templateRule.getKey()); return templateRule; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java index 57f205c5d2a..1b04d4b1876 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleDefinitionsLoaderTest.java @@ -21,6 +21,7 @@ package org.sonar.server.rule; import org.junit.Test; import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.server.plugins.ServerPluginRepository; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -30,7 +31,7 @@ public class RuleDefinitionsLoaderTest { @Test public void no_definitions() { CommonRuleDefinitions commonRulesDefinitions = mock(CommonRuleDefinitions.class); - RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions).load(); + RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class)).load(); assertThat(context.repositories()).isEmpty(); } @@ -38,9 +39,10 @@ public class RuleDefinitionsLoaderTest { @Test public void load_definitions() { CommonRuleDefinitions commonRulesDefinitions = mock(CommonRuleDefinitions.class); - RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, new RulesDefinition[] { - new FindbugsDefinitions(), new SquidDefinitions() - }).load(); + RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class), + new RulesDefinition[] { + new FindbugsDefinitions(), new SquidDefinitions() + }).load(); assertThat(context.repositories()).hasSize(2); assertThat(context.repository("findbugs")).isNotNull(); @@ -50,9 +52,10 @@ public class RuleDefinitionsLoaderTest { @Test public void define_common_rules() throws Exception { CommonRuleDefinitions commonRulesDefinitions = new FakeCommonRuleDefinitions(); - RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, new RulesDefinition[] { - new SquidDefinitions() - }).load(); + RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class), + new RulesDefinition[] { + new SquidDefinitions() + }).load(); assertThat(context.repositories()).extracting("key").containsOnly("squid", "common-java"); assertThat(context.repository("common-java").rules()).extracting("key").containsOnly("InsufficientBranchCoverage"); @@ -65,9 +68,10 @@ public class RuleDefinitionsLoaderTest { @Test public void plugin_common_rules_are_overridden() throws Exception { CommonRuleDefinitions commonRulesDefinitions = new FakeCommonRuleDefinitions(); - RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, new RulesDefinition[] { - new PluginCommonRuleDefinitions() - }).load(); + RulesDefinition.Context context = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), commonRulesDefinitions, mock(ServerPluginRepository.class), + new RulesDefinition[] { + new PluginCommonRuleDefinitions() + }).load(); assertThat(context.repositories()).extracting("key").containsOnly("common-java"); assertThat(context.repository("common-java").rules()).extracting("key").containsOnly("InsufficientBranchCoverage"); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java index 0e9b90fbc3c..c9690b1536c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java @@ -385,6 +385,7 @@ public interface RulesDefinition { */ class Context { private final Map repositoriesByKey = new HashMap<>(); + private String currentPluginKey; /** * New builder for {@link org.sonar.api.server.rule.RulesDefinition.Repository}. @@ -442,6 +443,10 @@ public interface RulesDefinition { } repositoriesByKey.put(newRepository.key, new RepositoryImpl(newRepository, existing)); } + + public void setCurrentPluginKey(@Nullable String pluginKey) { + this.currentPluginKey = pluginKey; + } } interface NewExtendedRepository { @@ -496,7 +501,7 @@ public interface RulesDefinition { @Override public NewRule createRule(String ruleKey) { checkArgument(!newRules.containsKey(ruleKey), "The rule '%s' of repository '%s' is declared several times", ruleKey, key); - NewRule newRule = new NewRule(key, ruleKey); + NewRule newRule = new NewRule(context.currentPluginKey, key, ruleKey); newRules.put(ruleKey, newRule); return newRule; } @@ -671,6 +676,7 @@ public interface RulesDefinition { } class NewRule { + private final String pluginKey; private final String repoKey; private final String key; private RuleType type; @@ -688,7 +694,8 @@ public interface RulesDefinition { private final DebtRemediationFunctions functions; private boolean activatedByDefault; - private NewRule(String repoKey, String key) { + private NewRule(@Nullable String pluginKey, String repoKey, String key) { + this.pluginKey = pluginKey; this.repoKey = repoKey; this.key = key; this.functions = new DefaultDebtRemediationFunctions(repoKey, key); @@ -926,6 +933,7 @@ public interface RulesDefinition { @Immutable class Rule { + private final String pluginKey; private final Repository repository; private final String repoKey; private final String key; @@ -944,6 +952,7 @@ public interface RulesDefinition { private final boolean activatedByDefault; private Rule(Repository repository, NewRule newRule) { + this.pluginKey = newRule.pluginKey; this.repository = repository; this.repoKey = newRule.repoKey; this.key = newRule.key; @@ -970,6 +979,14 @@ public interface RulesDefinition { return repository; } + /** + * @since 6.6 the plugin the rule was declared in + */ + @CheckForNull + public String pluginKey() { + return pluginKey; + } + public String key() { return key; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java index 06b6482500c..d8d9c6a9536 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginRepository.java @@ -94,6 +94,7 @@ public class ScannerPluginRepository implements PluginRepository, Startable { pluginInstancesByKeys.clear(); infosByKeys.clear(); + keysByClassLoader.clear(); } @Override