org.sonar.server.rule.CachingRuleFinder Maven / Gradle / Ivy
/*
* SonarQube
* Copyright (C) 2009-2018 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> FIND_BY_QUERY_ORDER = Ordering.natural().reverse().onResultOf(entry -> entry.getKey().getUpdatedAt());
private final Map rulesByRuleDefinition;
private final Map rulesById;
private final Map 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 buildRulesByRuleDefinitionDto(DbClient dbClient, DbSession dbSession) {
List dtos = dbClient.ruleDao().selectAllDefinitions(dbSession);
Set ruleKeys = dtos.stream().map(RuleDefinitionDto::getKey).collect(toSet(dtos.size()));
ListMultimap ruleParamsByRuleId = retrieveRuleParameters(dbClient, dbSession, ruleKeys);
Map 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 retrieveRuleParameters(DbClient dbClient, DbSession dbSession, Set 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 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 params) {
String severity = ruleDefinition.getSeverityString();
String description = ruleDefinition.getDescription();
RuleDto.Format descriptionFormat = ruleDefinition.getDescriptionFormat();
Rule apiRule = new 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 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;
}
}