import org.sonar.server.qualityprofile.ws.SearchDataLoader;
import org.sonar.server.root.ws.RootWsModule;
import org.sonar.server.rule.CommonRuleDefinitionsImpl;
-import org.sonar.server.rule.DefaultRuleFinder;
import org.sonar.server.rule.DeprecatedRulesDefinitionLoader;
import org.sonar.server.rule.RuleCreator;
import org.sonar.server.rule.RuleDefinitionsLoader;
import org.sonar.server.rule.RuleUpdater;
+import org.sonar.server.rule.WebServerRuleFinderImpl;
import org.sonar.server.rule.index.RuleIndexDefinition;
import org.sonar.server.rule.index.RuleIndexer;
import org.sonar.server.rule.ws.ActiveRuleCompleter;
RuleIndexer.class,
AnnotationRuleParser.class,
XMLRuleParser.class,
- DefaultRuleFinder.class,
+ WebServerRuleFinderImpl.class,
DeprecatedRulesDefinitionLoader.class,
RuleDefinitionsLoader.class,
CommonRuleDefinitionsImpl.class,
import org.sonar.server.qualityprofile.MassRegisterQualityProfiles;
import org.sonar.server.qualityprofile.RegisterQualityProfiles;
import org.sonar.server.rule.RegisterRules;
+import org.sonar.server.rule.WebServerRuleFinder;
import org.sonar.server.startup.DeleteOldAnalysisReportsFromFs;
import org.sonar.server.startup.DisplayLogOnDeprecatedProjects;
import org.sonar.server.startup.GeneratePluginIndex;
getOptional(IndexerStartupTask.class).ifPresent(IndexerStartupTask::execute);
get(ServerLifecycleNotifier.class).notifyStart();
get(ProcessCommandWrapper.class).notifyOperational();
+ get(WebServerRuleFinder.class).stopCaching();
}
});
--- /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.server.rule;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Ordering;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleParamDto;
+import org.sonar.markdown.Markdown;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+
+/**
+ * A {@link RuleFinder} implementation that retrieves all rule definitions and their parameter when instantiated, cache
+ * them in memory and provide implementation of {@link RuleFinder}'s method which only read from this data in memory.
+ */
+public class CachingRuleFinder implements RuleFinder {
+
+ private static final Ordering<Map.Entry<RuleDefinitionDto, Rule>> FIND_BY_QUERY_ORDER = Ordering.natural().reverse().onResultOf(entry -> entry.getKey().getUpdatedAt());
+
+ private final Map<RuleDefinitionDto, Rule> rulesByRuleDefinition;
+ private final Map<Integer, Rule> rulesById;
+ private final Map<RuleKey, Rule> rulesByKey;
+
+ public CachingRuleFinder(DbClient dbClient) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ this.rulesByRuleDefinition = buildRulesByRuleDefinitionDto(dbClient, dbSession);
+ this.rulesById = this.rulesByRuleDefinition.entrySet().stream()
+ .collect(uniqueIndex(entry -> entry.getKey().getId(), Map.Entry::getValue));
+ this.rulesByKey = this.rulesByRuleDefinition.entrySet().stream()
+ .collect(uniqueIndex(entry -> entry.getKey().getKey(), Map.Entry::getValue));
+ }
+ }
+
+ private static Map<RuleDefinitionDto, Rule> buildRulesByRuleDefinitionDto(DbClient dbClient, DbSession dbSession) {
+ List<RuleDefinitionDto> dtos = dbClient.ruleDao().selectAllDefinitions(dbSession);
+ Set<RuleKey> ruleKeys = dtos.stream().map(RuleDefinitionDto::getKey).collect(toSet(dtos.size()));
+ ListMultimap<Integer, RuleParamDto> ruleParamsByRuleId = retrieveRuleParameters(dbClient, dbSession, ruleKeys);
+ Map<RuleDefinitionDto, Rule> rulesByDefinition = new HashMap<>(dtos.size());
+ for (RuleDefinitionDto definition : dtos) {
+ rulesByDefinition.put(definition, toRule(definition, ruleParamsByRuleId.get(definition.getId())));
+ }
+ return ImmutableMap.copyOf(rulesByDefinition);
+ }
+
+ private static ImmutableListMultimap<Integer, RuleParamDto> retrieveRuleParameters(DbClient dbClient, DbSession dbSession, Set<RuleKey> ruleKeys) {
+ if (ruleKeys.isEmpty()) {
+ return ImmutableListMultimap.of();
+ }
+ return dbClient.ruleDao().selectRuleParamsByRuleKeys(dbSession, ruleKeys)
+ .stream()
+ .collect(MoreCollectors.index(RuleParamDto::getRuleId));
+ }
+
+ @Override
+ @Deprecated
+ @CheckForNull
+ public Rule findById(int ruleId) {
+ return rulesById.get(ruleId);
+ }
+
+ @Override
+ @CheckForNull
+ public Rule findByKey(@Nullable String repositoryKey, @Nullable String key) {
+ if (repositoryKey == null || key == null) {
+ return null;
+ }
+ return findByKey(RuleKey.of(repositoryKey, key));
+ }
+
+ @Override
+ @CheckForNull
+ public Rule findByKey(RuleKey key) {
+ return rulesByKey.get(key);
+ }
+
+ @Override
+ @CheckForNull
+ public Rule find(@Nullable RuleQuery query) {
+ if (query == null) {
+ return null;
+ }
+
+ return rulesByRuleDefinition.entrySet().stream()
+ .filter(entry -> matchQuery(entry.getKey(), query))
+ .sorted(FIND_BY_QUERY_ORDER)
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElse(null);
+ }
+
+ @Override
+ public Collection<Rule> findAll(@Nullable RuleQuery query) {
+ if (query == null) {
+ return Collections.emptyList();
+ }
+ return rulesByRuleDefinition.entrySet().stream()
+ .filter(entry -> matchQuery(entry.getKey(), query))
+ .sorted(FIND_BY_QUERY_ORDER)
+ .map(Map.Entry::getValue)
+ .collect(MoreCollectors.toList());
+ }
+
+ private static boolean matchQuery(RuleDefinitionDto ruleDefinition, RuleQuery ruleQuery) {
+ if (RuleStatus.REMOVED.equals(ruleDefinition.getStatus())) {
+ return false;
+ }
+ String repositoryKey = ruleQuery.getRepositoryKey();
+ if (ruleQuery.getRepositoryKey() != null && !repositoryKey.equals(ruleDefinition.getRepositoryKey())) {
+ return false;
+ }
+ String key = ruleQuery.getKey();
+ if (key != null && !key.equals(ruleDefinition.getRuleKey())) {
+ return false;
+ }
+ String configKey = ruleQuery.getConfigKey();
+ return configKey == null || configKey.equals(ruleDefinition.getConfigKey());
+ }
+
+ private static Rule toRule(RuleDefinitionDto ruleDefinition, List<RuleParamDto> params) {
+ String severity = ruleDefinition.getSeverityString();
+ String description = ruleDefinition.getDescription();
+ RuleDto.Format descriptionFormat = ruleDefinition.getDescriptionFormat();
+
+ org.sonar.api.rules.Rule apiRule = new org.sonar.api.rules.Rule();
+ apiRule
+ .setName(ruleDefinition.getName())
+ .setLanguage(ruleDefinition.getLanguage())
+ .setKey(ruleDefinition.getRuleKey())
+ .setConfigKey(ruleDefinition.getConfigKey())
+ .setIsTemplate(ruleDefinition.isTemplate())
+ .setCreatedAt(new Date(ruleDefinition.getCreatedAt()))
+ .setUpdatedAt(new Date(ruleDefinition.getUpdatedAt()))
+ .setRepositoryKey(ruleDefinition.getRepositoryKey())
+ .setSeverity(severity != null ? RulePriority.valueOf(severity) : null)
+ .setStatus(ruleDefinition.getStatus().name())
+ .setTags(ruleDefinition.getSystemTags().toArray(new String[ruleDefinition.getSystemTags().size()]))
+ .setId(ruleDefinition.getId());
+ if (description != null && descriptionFormat != null) {
+ if (RuleDto.Format.HTML.equals(descriptionFormat)) {
+ apiRule.setDescription(description);
+ } else {
+ apiRule.setDescription(Markdown.convertToHtml(description));
+ }
+ }
+
+ List<org.sonar.api.rules.RuleParam> apiParams = newArrayList();
+ for (RuleParamDto param : params) {
+ apiParams.add(new org.sonar.api.rules.RuleParam(apiRule, param.getName(), param.getDescription(), param.getType())
+ .setDefaultValue(param.getDefaultValue()));
+ }
+ apiRule.setParams(apiParams);
+
+ return apiRule;
+ }
+}
import static com.google.common.collect.Lists.newArrayList;
/**
- * Will be removed in the future. Please use {@link org.sonar.server.rule.RuleService}
+ * Will be removed in the future.
*/
public class DefaultRuleFinder implements RuleFinder {
private final Languages languages;
private final System2 system2;
private final OrganizationFlags organizationFlags;
+ private final WebServerRuleFinder webServerRuleFinder;
public RegisterRules(RuleDefinitionsLoader defLoader, RuleActivator ruleActivator, DbClient dbClient, RuleIndexer ruleIndexer,
- ActiveRuleIndexer activeRuleIndexer, Languages languages, System2 system2, OrganizationFlags organizationFlags) {
+ ActiveRuleIndexer activeRuleIndexer, Languages languages, System2 system2, OrganizationFlags organizationFlags,
+ WebServerRuleFinder webServerRuleFinder) {
this.defLoader = defLoader;
this.ruleActivator = ruleActivator;
this.dbClient = dbClient;
this.languages = languages;
this.system2 = system2;
this.organizationFlags = organizationFlags;
+ this.webServerRuleFinder = webServerRuleFinder;
}
@Override
RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key());
if (ruleDef.template() && orgsEnabled) {
RuleDefinitionDto ruleDefinition = allRules.get(ruleKey);
- if (ruleDefinition != null && ruleDefinition.getStatus() == RuleStatus.REMOVED) {
+ if (ruleDefinition != null && ruleDefinition.getStatus() == RuleStatus.REMOVED) {
LOG.debug("Template rule {} kept removed, because organizations are enabled.", ruleKey);
allRules.remove(ruleKey);
} else {
ruleIndexer.indexRuleDefinitions(keysToIndex);
activeRuleIndexer.index(changes);
profiler.stopDebug();
+
+ webServerRuleFinder.startCaching();
}
}
--- /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.server.rule;
+
+import org.sonar.api.rules.RuleFinder;
+
+/**
+ * {@link RuleFinder} implementation that supports caching used by the Web Server.
+ * <p>
+ * Caching is enabled right after loading of rules is done (see {@link RegisterRules}) and disabled
+ * once all startup tasks are done (see {@link org.sonar.server.platform.platformlevel.PlatformLevelStartup}).
+ * </p>
+ */
+public interface WebServerRuleFinder extends RuleFinder {
+ /**
+ * Enable caching.
+ */
+ void startCaching();
+
+ /**
+ * Disable caching.
+ */
+ void stopCaching();
+}
--- /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.server.rule;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Collection;
+import javax.annotation.CheckForNull;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.db.DbClient;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+
+public class WebServerRuleFinderImpl implements WebServerRuleFinder {
+ private final DbClient dbClient;
+ private final RuleFinder defaultFinder;
+ @VisibleForTesting
+ RuleFinder delegate;
+
+ public WebServerRuleFinderImpl(DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider) {
+ this.dbClient = dbClient;
+ this.defaultFinder = new DefaultRuleFinder(dbClient, defaultOrganizationProvider);
+ this.delegate = this.defaultFinder;
+ }
+
+ @Override
+ public void startCaching() {
+ this.delegate = new CachingRuleFinder(dbClient);
+ }
+
+ @Override
+ public void stopCaching() {
+ this.delegate = this.defaultFinder;
+ }
+
+ @Override
+ @CheckForNull
+ @Deprecated
+ public org.sonar.api.rules.Rule findById(int ruleId) {
+ return delegate.findById(ruleId);
+ }
+
+ @Override
+ @CheckForNull
+ public Rule findByKey(String repositoryKey, String key) {
+ return delegate.findByKey(repositoryKey, key);
+ }
+
+ @Override
+ @CheckForNull
+ public Rule findByKey(RuleKey key) {
+ return delegate.findByKey(key);
+ }
+
+ @Override
+ @CheckForNull
+ public Rule find(RuleQuery query) {
+ return delegate.find(query);
+ }
+
+ @Override
+ public Collection<Rule> findAll(RuleQuery query) {
+ return delegate.findAll(query);
+ }
+
+}
--- /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.server.rule;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Consumer;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.rule.RuleDao;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleParamDto;
+import org.sonar.db.rule.RuleTesting;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class CachingRuleFinderTest {
+ @org.junit.Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private DbClient dbClient = dbTester.getDbClient();
+
+ private AlwaysIncreasingSystem2 system2 = new AlwaysIncreasingSystem2();
+ private RuleDefinitionDto[] ruleDefinitions;
+ private RuleParamDto[] ruleParams;
+ private CachingRuleFinder underTest;
+
+ @Before()
+ public void setUp() throws Exception {
+ Consumer<RuleDefinitionDto> setUpdatedAt = rule -> rule.setUpdatedAt(system2.now());
+ this.ruleDefinitions = new RuleDefinitionDto[] {
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt),
+ dbTester.rules().insert(setUpdatedAt)
+ };
+ this.ruleParams = Arrays.stream(ruleDefinitions)
+ .map(rule -> dbTester.rules().insertRuleParam(rule))
+ .toArray(RuleParamDto[]::new);
+
+ underTest = new CachingRuleFinder(dbClient);
+
+ // delete all data from DB to ensure tests rely on cache exclusively
+ dbTester.executeUpdateSql("delete from rules");
+ dbTester.executeUpdateSql("delete from rules_parameters");
+ assertThat(dbTester.countRowsOfTable("rules")).isZero();
+ assertThat(dbTester.countRowsOfTable("rules_parameters")).isZero();
+ }
+
+ @Test
+ public void constructor_reads_rules_from_DB() {
+ DbClient dbClient = mock(DbClient.class);
+ DbSession dbSession = mock(DbSession.class);
+ RuleDao ruleDao = mock(RuleDao.class);
+ when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+ when(dbClient.ruleDao()).thenReturn(ruleDao);
+
+ new CachingRuleFinder(dbClient);
+
+ verify(dbClient).openSession(anyBoolean());
+ verify(ruleDao).selectAllDefinitions(dbSession);
+ verifyNoMoreInteractions(ruleDao);
+ }
+
+ @Test
+ public void constructor_reads_parameters_from_DB() {
+ DbClient dbClient = mock(DbClient.class);
+ DbSession dbSession = mock(DbSession.class);
+ RuleDao ruleDao = mock(RuleDao.class);
+ when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
+ when(dbClient.ruleDao()).thenReturn(ruleDao);
+ List<RuleKey> ruleKeys = Arrays.asList(RuleKey.of("A", "B"), RuleKey.of("C", "D"), RuleKey.of("E", "F"));
+ when(ruleDao.selectAllDefinitions(dbSession)).thenReturn(ruleKeys.stream().map(RuleTesting::newRule).collect(toList()));
+
+ new CachingRuleFinder(dbClient);
+
+ verify(ruleDao).selectRuleParamsByRuleKeys(dbSession, ImmutableSet.copyOf(ruleKeys));
+ }
+
+ @Test
+ public void findById_returns_all_loaded_rules_by_id() {
+ for (int i = 0; i < ruleDefinitions.length; i++) {
+ RuleDefinitionDto ruleDefinition = ruleDefinitions[i];
+ RuleParamDto ruleParam = ruleParams[i];
+
+ org.sonar.api.rules.Rule rule = underTest.findById(ruleDefinition.getId());
+ verifyRule(rule, ruleDefinition, ruleParam);
+ }
+ }
+
+ @Test
+ public void findById_returns_null_for_non_existing_id() {
+ assertThat(underTest.findById(new Random().nextInt())).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_all_loaded_rules_by_id() {
+ for (int i = 0; i < ruleDefinitions.length; i++) {
+ RuleDefinitionDto ruleDefinition = ruleDefinitions[i];
+ RuleParamDto ruleParam = ruleParams[i];
+
+ org.sonar.api.rules.Rule rule = underTest.findByKey(ruleDefinition.getKey());
+ verifyRule(rule, ruleDefinition, ruleParam);
+ assertThat(underTest.findByKey(ruleDefinition.getRepositoryKey(), ruleDefinition.getRuleKey()))
+ .isSameAs(rule);
+ }
+ }
+
+ @Test
+ public void findByKey_returns_null_when_RuleKey_is_null() {
+ assertThat(underTest.findByKey(null)).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_null_when_repository_key_is_null() {
+ assertThat(underTest.findByKey(null, randomAlphabetic(2))).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_null_when_key_is_null() {
+ assertThat(underTest.findByKey(randomAlphabetic(2), null)).isNull();
+ }
+
+ @Test
+ public void findByKey_returns_null_when_both_repository_key_and_key_are_null() {
+ assertThat(underTest.findByKey(null, null)).isNull();
+ }
+
+ @Test
+ public void find_returns_null_when_RuleQuery_is_empty() {
+ assertThat(underTest.find(null)).isNull();
+ }
+
+ @Test
+ public void find_returns_most_recent_rule_when_RuleQuery_has_no_non_null_field() {
+ Rule rule = underTest.find(RuleQuery.create());
+
+ assertThat(toRuleKey(rule)).isEqualTo(ruleDefinitions[5].getKey());
+ }
+
+ @Test
+ public void find_searches_by_exact_match_of_repository_key_and_returns_most_recent_rule() {
+ String repoKey = "ABCD";
+ RuleDefinitionDto[] sameRepoKey = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now()))
+ };
+ RuleDefinitionDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withRepositoryKey(repoKey))))
+ .isEqualTo(sameRepoKey[1].getKey());
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withRepositoryKey(otherRule.getRepositoryKey()))))
+ .isEqualTo(otherRule.getKey());
+ assertThat(underTest.find(RuleQuery.create().withRepositoryKey(repoKey.toLowerCase())))
+ .isNull();
+ assertThat(underTest.find(RuleQuery.create().withRepositoryKey(randomAlphabetic(3))))
+ .isNull();
+ }
+
+ @Test
+ public void find_searches_by_exact_match_of_ruleKey_and_returns_most_recent_rule() {
+ String ruleKey = "ABCD";
+ RuleDefinitionDto[] sameRuleKey = {
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now()))
+ };
+ RuleDefinitionDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withKey(ruleKey))))
+ .isEqualTo(sameRuleKey[1].getKey());
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withKey(otherRule.getRuleKey()))))
+ .isEqualTo(otherRule.getKey());
+ assertThat(underTest.find(RuleQuery.create().withKey(ruleKey.toLowerCase())))
+ .isNull();
+ assertThat(underTest.find(RuleQuery.create().withKey(randomAlphabetic(3))))
+ .isNull();
+ }
+
+ @Test
+ public void find_searches_by_exact_match_of_configKey_and_returns_most_recent_rule() {
+ String configKey = "ABCD";
+ RuleDefinitionDto[] sameConfigKey = {
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now()))
+ };
+ RuleDefinitionDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withConfigKey(configKey))))
+ .isEqualTo(sameConfigKey[1].getKey());
+ assertThat(toRuleKey(underTest.find(RuleQuery.create().withConfigKey(otherRule.getConfigKey()))))
+ .isEqualTo(otherRule.getKey());
+ assertThat(underTest.find(RuleQuery.create().withConfigKey(configKey.toLowerCase())))
+ .isNull();
+ assertThat(underTest.find(RuleQuery.create().withConfigKey(randomAlphabetic(3))))
+ .isNull();
+ }
+
+ @Test
+ public void find_searches_by_exact_match_and_match_on_all_criterias_and_returns_most_recent_match() {
+ String repoKey = "ABCD";
+ String ruleKey = "EFGH";
+ String configKey = "IJKL";
+ RuleDefinitionDto[] rules = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()))
+ };
+ RuleQuery allQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery ruleAndConfigKeyQuery = RuleQuery.create().withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery repoAndConfigKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withConfigKey(configKey);
+ RuleQuery repoAndKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey);
+ RuleQuery configKeyQuery = RuleQuery.create().withConfigKey(configKey);
+ RuleQuery ruleKeyQuery = RuleQuery.create().withKey(ruleKey);
+ RuleQuery repoKeyQuery = RuleQuery.create().withRepositoryKey(repoKey);
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(toRuleKey(underTest.find(allQuery))).isEqualTo(rules[0].getKey());
+ assertThat(toRuleKey(underTest.find(ruleAndConfigKeyQuery))).isEqualTo(rules[1].getKey());
+ assertThat(toRuleKey(underTest.find(repoAndConfigKeyQuery))).isEqualTo(rules[2].getKey());
+ assertThat(toRuleKey(underTest.find(repoAndKeyQuery))).isEqualTo(rules[0].getKey());
+ assertThat(toRuleKey(underTest.find(repoKeyQuery))).isEqualTo(rules[2].getKey());
+ assertThat(toRuleKey(underTest.find(ruleKeyQuery))).isEqualTo(rules[1].getKey());
+ assertThat(toRuleKey(underTest.find(configKeyQuery))).isEqualTo(rules[2].getKey());
+ }
+
+ @Test
+ public void findAll_returns_empty_when_RuleQuery_is_empty() {
+ assertThat(underTest.findAll(null)).isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_when_RuleQuery_has_no_non_null_field() {
+ assertThat(underTest.findAll(RuleQuery.create()))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsOnly(Arrays.stream(ruleDefinitions).map(RuleDefinitionDto::getKey).toArray(RuleKey[]::new));
+ }
+
+ @Test
+ public void findAll_returns_all_rules_with_exact_same_repository_key_and_order_them_most_recent_first() {
+ String repoKey = "ABCD";
+ RuleDefinitionDto[] sameRepoKey = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setUpdatedAt(system2.now()))
+ };
+ RuleDefinitionDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(repoKey)))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(sameRepoKey[1].getKey(), sameRepoKey[0].getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(otherRule.getRepositoryKey())))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(otherRule.getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(repoKey.toLowerCase())))
+ .isEmpty();
+ assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey(randomAlphabetic(3))))
+ .isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_with_exact_same_rulekey_and_order_them_most_recent_first() {
+ String ruleKey = "ABCD";
+ RuleDefinitionDto[] sameRuleKey = {
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setUpdatedAt(system2.now()))
+ };
+ RuleDefinitionDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(underTest.findAll(RuleQuery.create().withKey(ruleKey)))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(sameRuleKey[1].getKey(), sameRuleKey[0].getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withKey(otherRule.getRuleKey())))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(otherRule.getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withKey(ruleKey.toLowerCase())))
+ .isEmpty();
+ assertThat(underTest.findAll(RuleQuery.create().withKey(randomAlphabetic(3))))
+ .isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_with_exact_same_configkey_and_order_them_most_recent_first() {
+ String configKey = "ABCD";
+ RuleDefinitionDto[] sameConfigKey = {
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setConfigKey(configKey).setUpdatedAt(system2.now()))
+ };
+ RuleDefinitionDto otherRule = dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()));
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(configKey)))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(sameConfigKey[1].getKey(), sameConfigKey[0].getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(otherRule.getConfigKey())))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(otherRule.getKey());
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(configKey.toLowerCase())))
+ .isEmpty();
+ assertThat(underTest.findAll(RuleQuery.create().withConfigKey(randomAlphabetic(3))))
+ .isEmpty();
+ }
+
+ @Test
+ public void findAll_returns_all_rules_which_match_exactly_all_criteria_and_order_then_by_most_recent_first() {
+ String repoKey = "ABCD";
+ String ruleKey = "EFGH";
+ String configKey = "IJKL";
+ RuleDefinitionDto[] rules = {
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRuleKey(ruleKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setRepositoryKey(repoKey).setConfigKey(configKey).setUpdatedAt(system2.now())),
+ dbTester.rules().insert(rule -> rule.setUpdatedAt(system2.now()))
+ };
+ RuleQuery allQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery ruleAndConfigKeyQuery = RuleQuery.create().withKey(ruleKey).withConfigKey(configKey);
+ RuleQuery repoAndConfigKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withConfigKey(configKey);
+ RuleQuery repoAndKeyQuery = RuleQuery.create().withRepositoryKey(repoKey).withKey(ruleKey);
+ RuleQuery configKeyQuery = RuleQuery.create().withConfigKey(configKey);
+ RuleQuery ruleKeyQuery = RuleQuery.create().withKey(ruleKey);
+ RuleQuery repoKeyQuery = RuleQuery.create().withRepositoryKey(repoKey);
+
+ CachingRuleFinder underTest = new CachingRuleFinder(dbClient);
+
+ assertThat(underTest.findAll(allQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[0].getKey());
+ assertThat(underTest.findAll(ruleAndConfigKeyQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[1].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(repoAndConfigKeyQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[2].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(repoAndKeyQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[0].getKey());
+ assertThat(underTest.findAll(repoKeyQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[2].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(ruleKeyQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[1].getKey(), rules[0].getKey());
+ assertThat(underTest.findAll(configKeyQuery))
+ .extracting(CachingRuleFinderTest::toRuleKey)
+ .containsExactly(rules[2].getKey(), rules[1].getKey(), rules[0].getKey());
+ }
+
+ private static RuleKey toRuleKey(Rule rule) {
+ return RuleKey.of(rule.getRepositoryKey(), rule.getKey());
+ }
+
+ private void verifyRule(Rule rule, RuleDefinitionDto ruleDefinition, RuleParamDto ruleParam) {
+ assertThat(rule).isNotNull();
+
+ assertThat(rule.getName()).isEqualTo(ruleDefinition.getName());
+ assertThat(rule.getLanguage()).isEqualTo(ruleDefinition.getLanguage());
+ assertThat(rule.getKey()).isEqualTo(ruleDefinition.getRuleKey());
+ assertThat(rule.getConfigKey()).isEqualTo(ruleDefinition.getConfigKey());
+ assertThat(rule.isTemplate()).isEqualTo(ruleDefinition.isTemplate());
+ assertThat(rule.getCreatedAt().getTime()).isEqualTo(ruleDefinition.getCreatedAt());
+ assertThat(rule.getUpdatedAt().getTime()).isEqualTo(ruleDefinition.getUpdatedAt());
+ assertThat(rule.getRepositoryKey()).isEqualTo(ruleDefinition.getRepositoryKey());
+ assertThat(rule.getSeverity().name()).isEqualTo(ruleDefinition.getSeverityString());
+ assertThat(rule.getTags()).isEqualTo(ruleDefinition.getSystemTags().stream().toArray(String[]::new));
+ assertThat(rule.getId()).isEqualTo(ruleDefinition.getId());
+ assertThat(rule.getDescription()).isEqualTo(ruleDefinition.getDescription());
+
+ assertThat(rule.getParams()).hasSize(1);
+ org.sonar.api.rules.RuleParam param = rule.getParams().iterator().next();
+ assertThat(param.getRule()).isSameAs(rule);
+ assertThat(param.getKey()).isEqualTo(ruleParam.getName());
+ assertThat(param.getDescription()).isEqualTo(ruleParam.getDescription());
+ assertThat(param.getType()).isEqualTo(ruleParam.getType());
+ assertThat(param.getDefaultValue()).isEqualTo(ruleParam.getDefaultValue());
+ }
+}
import org.sonar.server.rule.index.RuleQuery;
import static com.google.common.collect.Sets.newHashSet;
-import static java.util.Arrays.asList;
+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.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.INFO;
public LogTester logTester = new LogTester();
private RuleActivator ruleActivator = mock(RuleActivator.class);
+ private WebServerRuleFinder webServerRuleFinder = mock(WebServerRuleFinder.class);
private DbClient dbClient = dbTester.getDbClient();
private RuleIndexer ruleIndexer;
private ActiveRuleIndexer activeRuleIndexer;
public void delete_repositories_that_have_been_uninstalled() {
RuleRepositoryDto repository = new RuleRepositoryDto("findbugs", "java", "Findbugs");
DbSession dbSession = dbTester.getSession();
- dbTester.getDbClient().ruleRepositoryDao().insert(dbSession, asList(repository));
+ dbTester.getDbClient().ruleRepositoryDao().insert(dbSession, singletonList(repository));
dbSession.commit();
execute(new FakeRepositoryV1());
// user adds tags and sets markdown note
OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
- String organizationUuid = defaultOrganization.getUuid();
RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY1);
rule1.setTags(newHashSet("usertag1", "usertag2"));
rule1.setNoteData("user *note*");
});
OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
- String organizationUuid = defaultOrganization.getUuid();
RuleDto rule = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY1);
assertThat(rule.getSystemTags()).containsOnly("tag1");
when(system.now()).thenReturn(DATE2.getTime());
execute();
- String organizationUuid = dbTester.getDefaultOrganization().getUuid();
RuleDto rule = dbClient.ruleDao().selectOrFailByKey(dbTester.getSession(), defaultOrganization, RULE_KEY1);
assertThat(rule.getStatus()).isEqualTo(RuleStatus.REMOVED);
assertThat(ruleIndex.search(new RuleQuery().setKey(RULE_KEY1.toString()), new SearchOptions()).getTotal()).isEqualTo(0);
RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(DeprecatedRulesDefinitionLoader.class), mock(CommonRuleDefinitionsImpl.class), defs);
Languages languages = mock(Languages.class);
when(languages.get("java")).thenReturn(mock(Language.class));
+ reset(webServerRuleFinder);
- RegisterRules task = new RegisterRules(loader, ruleActivator, dbClient, ruleIndexer, activeRuleIndexer, languages, system, organizationFlags);
+ RegisterRules task = new RegisterRules(loader, ruleActivator, dbClient, ruleIndexer, activeRuleIndexer, languages, system, organizationFlags, webServerRuleFinder);
task.start();
// Execute a commit to refresh session state as the task is using its own session
dbTester.getSession().commit();
+
+ verify(webServerRuleFinder).startCaching();
}
private RuleParamDto getParam(List<RuleParamDto> params, String key) {
--- /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.server.rule;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.db.DbClient;
+import org.sonar.db.rule.RuleDao;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WebServerRuleFinderImplTest {
+
+ private DbClient dbClient = mock(DbClient.class);
+ private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.fromUuid("1111");
+ private WebServerRuleFinderImpl underTest = new WebServerRuleFinderImpl(dbClient, defaultOrganizationProvider);
+
+ @Before
+ public void setUp() throws Exception {
+ when(dbClient.ruleDao()).thenReturn(mock(RuleDao.class));
+ }
+
+ @Test
+ public void constructor_initializes_with_non_caching_delegate() {
+ assertThat(underTest.delegate).isInstanceOf(DefaultRuleFinder.class);
+ }
+
+ @Test
+ public void startCaching_sets_caching_delegate() {
+ underTest.startCaching();
+
+ assertThat(underTest.delegate).isInstanceOf(CachingRuleFinder.class);
+ }
+
+ @Test
+ public void stopCaching_restores_non_caching_delegate() {
+ RuleFinder nonCachingDelegate = underTest.delegate;
+
+ underTest.startCaching();
+ underTest.stopCaching();
+
+ assertThat(underTest.delegate).isSameAs(nonCachingDelegate);
+ }
+}