return mapper(session).selectParamsByRuleKey(key);
}
- public List<RuleParamDto> selectRuleParamsByRuleKeys(DbSession session, List<RuleKey> ruleKeys) {
+ public List<RuleParamDto> selectRuleParamsByRuleKeys(DbSession session, Collection<RuleKey> ruleKeys) {
return executeLargeInputs(ruleKeys, mapper(session)::selectParamsByRuleKeys);
}
import org.sonar.server.platform.ServerLifecycleNotifier;
import org.sonar.server.platform.web.RegisterServletFilters;
import org.sonar.server.qualitygate.RegisterQualityGates;
-import org.sonar.server.qualityprofile.CachingDefinedQProfileCreationImpl;
-import org.sonar.server.qualityprofile.CachingRuleActivator;
-import org.sonar.server.qualityprofile.CachingRuleActivatorContextFactory;
+import org.sonar.server.qualityprofile.DefinedQProfileInsertImpl;
import org.sonar.server.qualityprofile.DefinedQProfileLoader;
import org.sonar.server.qualityprofile.RegisterQualityProfiles;
import org.sonar.server.rule.RegisterRules;
RegisterRules.class);
add(DefinedQProfileLoader.class);
addIfStartupLeader(
- CachingRuleActivatorContextFactory.class,
- CachingRuleActivator.class,
- CachingDefinedQProfileCreationImpl.class,
+ DefinedQProfileInsertImpl.class,
RegisterQualityProfiles.class,
RegisterPermissionTemplates.class,
RenameDeprecatedPropertyKeys.class,
+++ /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.qualityprofile;
-
-public interface CachingDefinedQProfileCreation extends DefinedQProfileCreation {
-}
+++ /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.qualityprofile;
-
-import org.sonar.db.DbClient;
-
-public class CachingDefinedQProfileCreationImpl extends DefinedQProfileCreationImpl implements CachingDefinedQProfileCreation {
- public CachingDefinedQProfileCreationImpl(DbClient dbClient, QProfileFactory profileFactory, CachingRuleActivator ruleActivator) {
- super(dbClient, profileFactory, ruleActivator);
- }
-}
+++ /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.qualityprofile;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import java.util.List;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.qualityprofile.QualityProfileDto;
-import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
-import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.user.UserSession;
-import org.sonar.server.util.TypeValidations;
-
-public class CachingRuleActivator extends RuleActivator {
- private final Cache<String, List<QualityProfileDto>> childrenByParentKey = CacheBuilder.newBuilder()
- .maximumSize(10_000)
- .build();
-
- public CachingRuleActivator(System2 system2, DbClient db, RuleIndex ruleIndex, CachingRuleActivatorContextFactory contextFactory, TypeValidations typeValidations,
- ActiveRuleIndexer activeRuleIndexer, UserSession userSession) {
- super(system2, db, ruleIndex, contextFactory, typeValidations, activeRuleIndexer, userSession);
- }
-
- @Override
- protected List<QualityProfileDto> getChildren(DbSession session, String qualityProfileKey) {
- List<QualityProfileDto> res = childrenByParentKey.getIfPresent(qualityProfileKey);
- if (res != null) {
- return res;
- }
- res = super.getChildren(session, qualityProfileKey);
- childrenByParentKey.put(qualityProfileKey, res);
- return res;
- }
-
-}
+++ /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.qualityprofile;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import org.picocontainer.Startable;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.qualityprofile.ActiveRuleDto;
-import org.sonar.db.qualityprofile.ActiveRuleKey;
-import org.sonar.db.rule.RuleDefinitionDto;
-
-public class CachingRuleActivatorContextFactory extends RuleActivatorContextFactory implements Startable {
- private final DbClient dbClient;
- private final Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = new HashMap<>();
- private final Cache<String, Map<RuleKey, ActiveRuleDto>> childrenByParentKey = CacheBuilder.newBuilder()
- .maximumSize(10)
- .build();
-
- public CachingRuleActivatorContextFactory(DbClient db) {
- super(db);
- this.dbClient = db;
- }
-
- @Override
- public void start() {
- try (DbSession dbSession = dbClient.openSession(false)) {
- dbClient.ruleDao().selectAllDefinitions(dbSession).forEach(rule -> rulesByRuleKey.put(rule.getKey(), rule));
- }
- }
-
- @Override
- public void stop() {
- // nothing to do
- }
-
- @Override
- Optional<RuleDefinitionDto> getRule(DbSession dbSession, RuleKey ruleKey) {
- return Optional.ofNullable(rulesByRuleKey.get(ruleKey));
- }
-
- @Override
- Optional<ActiveRuleDto> getActiveRule(DbSession session, ActiveRuleKey key) {
- try {
- String profileKey = key.qProfile();
- Map<RuleKey, ActiveRuleDto> profileActiveRulesByRuleKey = childrenByParentKey.get(
- profileKey,
- () -> loadActiveRulesOfQualityProfile(session, profileKey));
- return Optional.ofNullable(profileActiveRulesByRuleKey.get(key.ruleKey()));
- } catch (ExecutionException e) {
- throw new IllegalStateException(e.getCause());
- }
- }
-
- private Map<RuleKey, ActiveRuleDto> loadActiveRulesOfQualityProfile(DbSession session, String profileKey) {
- return dbClient.activeRuleDao().selectByProfileKey(session, profileKey).stream()
- .collect(MoreCollectors.uniqueIndex(dto -> dto.getKey().ruleKey()));
- }
-}
--- /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.qualityprofile;
+
+import java.util.List;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+
+public interface DefinedQProfileInsert {
+ void create(DbSession session, DefinedQProfile definedQProfile, OrganizationDto organization, List<ActiveRuleChange> changes);
+}
--- /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.qualityprofile;
+
+import com.google.common.base.Splitter;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.ActiveRuleParam;
+import org.sonar.api.server.rule.RuleParamType;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.loadedtemplate.LoadedTemplateDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.qualityprofile.ActiveRuleParamDto;
+import org.sonar.db.qualityprofile.QualityProfileDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleParamDto;
+import org.sonar.server.util.TypeValidations;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Lists.newArrayList;
+
+public class DefinedQProfileInsertImpl implements DefinedQProfileInsert {
+ private final DbClient dbClient;
+ private final System2 system2;
+ private final UuidFactory uuidFactory;
+ private final TypeValidations typeValidations;
+ private RegisterQualityProfiles.RuleRepository ruleRepository;
+
+ public DefinedQProfileInsertImpl(DbClient dbClient, System2 system2, UuidFactory uuidFactory, TypeValidations typeValidations) {
+ this.dbClient = dbClient;
+ this.system2 = system2;
+ this.uuidFactory = uuidFactory;
+ this.typeValidations = typeValidations;
+ }
+
+ @Override
+ public void create(DbSession session, DefinedQProfile definedQProfile, OrganizationDto organization, List<ActiveRuleChange> changes) {
+ initRuleRepository(session);
+
+ checkArgument(definedQProfile.getParentQProfileName() == null,
+ "Inheritance of Quality Profiles is not supported yet");
+ // Optional.ofNullable(definedQProfile.getParentQProfileName())
+ // .map(parentQProfileName -> dbClient.qualityProfileDao().selectByNameAndLanguage(organization, parentQProfileName.getName(),
+ // parentQProfileName.getLanguage(), session))
+ // .map(parentQualityProfileDto -> dbClient.activeRuleDao().selectByRuleIds());
+
+ Date now = new Date(system2.now());
+ QualityProfileDto profileDto = insertQualityProfile(session, definedQProfile, organization, now);
+
+ List<ActiveRuleChange> localChanges = definedQProfile.getActiveRules()
+ .stream()
+ .map(activeRule -> insertActiveRule(session, profileDto, activeRule, now.getTime()))
+ .collect(MoreCollectors.toList());
+
+ localChanges.forEach(change -> dbClient.qProfileChangeDao().insert(session, change.toDto(null)));
+
+ insertTemplate(session, definedQProfile, organization);
+
+ changes.addAll(localChanges);
+ }
+
+ private void initRuleRepository(DbSession session) {
+ if (ruleRepository == null) {
+ ruleRepository = new RegisterQualityProfiles.RuleRepository(dbClient, session);
+ }
+ }
+
+ private QualityProfileDto insertQualityProfile(DbSession session, DefinedQProfile definedQProfile, OrganizationDto organization, Date now) {
+ QualityProfileDto profileDto = QualityProfileDto.createFor(uuidFactory.create())
+ .setName(definedQProfile.getName())
+ .setOrganizationUuid(organization.getUuid())
+ .setLanguage(definedQProfile.getLanguage())
+ .setDefault(definedQProfile.isDefault())
+ .setRulesUpdatedAtAsDate(now);
+ dbClient.qualityProfileDao().insert(session, profileDto);
+ return profileDto;
+ }
+
+ private ActiveRuleChange insertActiveRule(DbSession session, QualityProfileDto profileDto, org.sonar.api.rules.ActiveRule activeRule, long now) {
+ RuleKey ruleKey = RuleKey.of(activeRule.getRepositoryKey(), activeRule.getRuleKey());
+ RuleDefinitionDto ruleDefinitionDto = ruleRepository.getDefinition(ruleKey)
+ .orElseThrow(() -> new IllegalStateException("RuleDefinition not found for key " + ruleKey));
+
+ ActiveRuleDto dto = ActiveRuleDto.createFor(profileDto, ruleDefinitionDto);
+ dto.setSeverity(
+ firstNonNull(
+ activeRule.getSeverity().name(),
+ // context.parentSeverity(),
+ ruleDefinitionDto.getSeverityString()));
+ // ActiveRule.Inheritance inheritance = activeRule.getInheritance();
+ // if (inheritance != null) {
+ // activeRule.setInheritance(inheritance.name());
+ // }
+ dto.setUpdatedAt(now);
+ dto.setCreatedAt(now);
+ dbClient.activeRuleDao().insert(session, dto);
+
+ List<ActiveRuleParamDto> paramDtos = insertActiveRuleParams(session, activeRule, ruleKey, dto);
+
+ ActiveRuleChange change = ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(profileDto.getKey(), ruleKey));
+ change.setSeverity(dto.getSeverityString());
+ paramDtos.forEach(paramDto -> change.setParameter(paramDto.getKey(), paramDto.getValue()));
+ return change;
+ }
+
+ private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, org.sonar.api.rules.ActiveRule activeRule, RuleKey ruleKey, ActiveRuleDto activeRuleDto) {
+ Map<String, String> valuesByParamKey = activeRule.getActiveRuleParams()
+ .stream()
+ .collect(MoreCollectors.uniqueIndex(ActiveRuleParam::getParamKey, ActiveRuleParam::getValue));
+ return ruleRepository.getRuleParams(ruleKey)
+ .stream()
+ .map(param -> {
+ String activeRuleValue = valuesByParamKey.get(param.getName());
+ return createParamDto(param, activeRuleValue == null ? param.getDefaultValue() : activeRuleValue);
+ })
+ .filter(Objects::nonNull)
+ .peek(paramDto -> dbClient.activeRuleDao().insertParam(session, activeRuleDto, paramDto))
+ .collect(MoreCollectors.toList());
+ }
+
+ @CheckForNull
+ private ActiveRuleParamDto createParamDto(RuleParamDto param, @Nullable String value) {
+ if (value == null) {
+ return null;
+ }
+ ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(param);
+ paramDto.setValue(validateParam(param, value));
+ return paramDto;
+ }
+
+ @CheckForNull
+ private String validateParam(RuleParamDto ruleParam, String value) {
+ RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType());
+ if (ruleParamType.multiple()) {
+ List<String> values = newArrayList(Splitter.on(",").split(value));
+ typeValidations.validate(values, ruleParamType.type(), ruleParamType.values());
+ } else {
+ typeValidations.validate(value, ruleParamType.type(), ruleParamType.values());
+ }
+ return value;
+ }
+
+ private void insertTemplate(DbSession session, DefinedQProfile qualityProfile, OrganizationDto organization) {
+ LoadedTemplateDto template = new LoadedTemplateDto(organization.getUuid(), qualityProfile.getLoadedTemplateType());
+ dbClient.loadedTemplateDao().insert(template, session);
+ }
+}
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.lowerCase;
*/
private ListMultimap<String, RulesProfile> buildRulesProfilesByLanguage() {
ListMultimap<String, RulesProfile> byLang = ArrayListMultimap.create();
+ Profiler profiler = Profiler.create(Loggers.get(getClass()));
for (ProfileDefinition definition : definitions) {
+ profiler.start();
ValidationMessages validation = ValidationMessages.create();
RulesProfile profile = definition.createProfile(validation);
validation.log(LOGGER);
- if (profile != null && !validation.hasErrors()) {
- checkArgument(isNotEmpty(profile.getName()), "Profile created by Definition %s can't have a blank name", definition);
- byLang.put(lowerCase(profile.getLanguage(), Locale.ENGLISH), profile);
+ if (profile == null) {
+ profiler.stopDebug(format("Loaded definition %s that return no profile", definition));
+ } else {
+ if (!validation.hasErrors()) {
+ checkArgument(isNotEmpty(profile.getName()), "Profile created by Definition %s can't have a blank name", definition);
+ byLang.put(lowerCase(profile.getLanguage(), Locale.ENGLISH), profile);
+ }
+ profiler.stopDebug(format("Loaded definition %s for language %s", profile.getName(), profile.getLanguage()));
}
}
return byLang;
.collect(MoreCollectors.uniqueIndex(Map.Entry::getKey, entry -> toQualityProfiles(entry.getValue()), buildersByLanguage.size()));
}
- private boolean ensureParentExists(String language, List<DefinedQProfile.Builder> builders) {
+ private static boolean ensureParentExists(String language, List<DefinedQProfile.Builder> builders) {
Set<String> qProfileNames = builders.stream()
.map(DefinedQProfile.Builder::getName)
.collect(MoreCollectors.toSet(builders.size()));
*/
package org.sonar.server.qualityprofile;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.Pagination;
import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleParamDto;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
import static org.sonar.db.Pagination.forPage;
/**
private final DefinedQProfileRepository definedQProfileRepository;
private final DbClient dbClient;
- private final DefinedQProfileCreation definedQProfileCreation;
private final ActiveRuleIndexer activeRuleIndexer;
+ private final DefinedQProfileInsert definedQProfileInsert;
public RegisterQualityProfiles(DefinedQProfileRepository definedQProfileRepository,
- DbClient dbClient, DefinedQProfileCreation definedQProfileCreation, ActiveRuleIndexer activeRuleIndexer) {
+ DbClient dbClient, DefinedQProfileInsert definedQProfileInsert, ActiveRuleIndexer activeRuleIndexer) {
this.definedQProfileRepository = definedQProfileRepository;
this.dbClient = dbClient;
- this.definedQProfileCreation = definedQProfileCreation;
this.activeRuleIndexer = activeRuleIndexer;
+ this.definedQProfileInsert = definedQProfileInsert;
}
public void start() {
Profiler profiler = Profiler.create(Loggers.get(getClass())).startInfo("Register quality profiles");
+ if (definedQProfileRepository.getQProfilesByLanguage().isEmpty()) {
+ return;
+ }
try (DbSession session = dbClient.openSession(false)) {
List<ActiveRuleChange> changes = new ArrayList<>();
- definedQProfileRepository.getQProfilesByLanguage().entrySet()
- .forEach(entry -> registerPerLanguage(session, entry.getValue(), changes));
+ definedQProfileRepository.getQProfilesByLanguage()
+ .forEach((key, value) -> registerPerLanguage(session, value, changes));
activeRuleIndexer.index(changes);
profiler.stopDebug();
}
}
private void registerPerLanguage(DbSession session, List<DefinedQProfile> qualityProfiles, List<ActiveRuleChange> changes) {
- qualityProfiles.forEach(qp -> registerPerQualityProfile(session, qp, changes));
+ qualityProfiles.stream()
+ .sorted(new SortByParentName(qualityProfiles))
+ .forEach(qp -> registerPerQualityProfile(session, qp, changes));
session.commit();
}
private void registerPerQualityProfile(DbSession session, DefinedQProfile qualityProfile, List<ActiveRuleChange> changes) {
LOGGER.info("Register profile {}", qualityProfile.getQProfileName());
+ Profiler profiler = Profiler.create(Loggers.get(getClass()));
List<OrganizationDto> organizationDtos;
while (!(organizationDtos = getOrganizationsWithoutQP(session, qualityProfile)).isEmpty()) {
- organizationDtos.forEach(organization -> registerPerQualityProfileAndOrganization(session, qualityProfile, organization, changes));
+ organizationDtos.forEach(organization -> registerPerQualityProfileAndOrganization(session, qualityProfile, organization, changes, profiler));
}
}
qualityProfile.getLoadedTemplateType(), PROCESSED_ORGANIZATIONS_BATCH_SIZE);
}
- private void registerPerQualityProfileAndOrganization(DbSession session, DefinedQProfile qualityProfile, OrganizationDto organization, List<ActiveRuleChange> changes) {
- LOGGER.debug("Register profile {} for organization {}", qualityProfile.getQProfileName(), organization.getKey());
+ private void registerPerQualityProfileAndOrganization(DbSession session,
+ DefinedQProfile definedQProfile, OrganizationDto organization, List<ActiveRuleChange> changes, Profiler profiler) {
+ profiler.start();
+
+ definedQProfileInsert.create(session, definedQProfile, organization, changes);
- definedQProfileCreation.create(session, qualityProfile, organization, changes);
session.commit();
+
+ profiler.stopDebug(format("Register profile %s for organization %s", definedQProfile.getQProfileName(), organization.getKey()));
+ }
+
+ @VisibleForTesting
+ static class SortByParentName implements Comparator<DefinedQProfile> {
+ private final Map<String, DefinedQProfile> buildersByName;
+ @VisibleForTesting
+ final Map<String, Integer> depthByBuilder;
+
+ @VisibleForTesting
+ SortByParentName(Collection<DefinedQProfile> builders) {
+ buildersByName = builders.stream()
+ .collect(MoreCollectors.uniqueIndex(DefinedQProfile::getName, Function.identity(), builders.size()));
+ Map<String, Integer> depthByBuilder = new HashMap<>();
+ builders.forEach(builder -> depthByBuilder.put(builder.getName(), 0));
+ builders.forEach(builder -> increaseDepth(buildersByName, depthByBuilder, builder));
+ this.depthByBuilder = ImmutableMap.copyOf(depthByBuilder);
+ }
+
+ private void increaseDepth(Map<String, DefinedQProfile> buildersByName, Map<String, Integer> maps, DefinedQProfile builder) {
+ Optional.ofNullable(builder.getParentQProfileName())
+ .ifPresent(parentQProfileName -> {
+ DefinedQProfile parent = buildersByName.get(parentQProfileName.getName());
+ if (parent.getParentQProfileName() != null) {
+ increaseDepth(buildersByName, maps, parent);
+ }
+ maps.put(builder.getName(), maps.get(parent.getName()) + 1);
+ });
+ }
+
+ @Override
+ public int compare(DefinedQProfile o1, DefinedQProfile o2) {
+ return depthByBuilder.getOrDefault(o1.getName(), 0) - depthByBuilder.getOrDefault(o2.getName(), 0);
+ }
}
+ public static class RuleRepository {
+ private final Map<RuleKey, RuleDefinitionDto> ruleDefinitions;
+ private final Map<RuleKey, Set<RuleParamDto>> ruleParams;
+
+ public RuleRepository(DbClient dbClient, DbSession session) {
+ this.ruleDefinitions = dbClient.ruleDao().selectAllDefinitions(session)
+ .stream()
+ .collect(Collectors.toMap(RuleDefinitionDto::getKey, Function.identity()));
+ Map<Integer, RuleKey> ruleIdsByKey = ruleDefinitions.values()
+ .stream()
+ .collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getId, RuleDefinitionDto::getKey));
+ this.ruleParams = new HashMap<>(ruleIdsByKey.size());
+ dbClient.ruleDao().selectRuleParamsByRuleKeys(session, ruleDefinitions.keySet())
+ .forEach(ruleParam -> ruleParams.compute(
+ ruleIdsByKey.get(ruleParam.getRuleId()),
+ (key, value) -> {
+ if (value == null) {
+ return ImmutableSet.of(ruleParam);
+ }
+ return ImmutableSet.copyOf(Sets.union(value, Collections.singleton(ruleParam)));
+ }));
+ }
+
+ public Optional<RuleDefinitionDto> getDefinition(RuleKey ruleKey) {
+ return Optional.ofNullable(ruleDefinitions.get(requireNonNull(ruleKey, "RuleKey can't be null")));
+ }
+
+ public Set<RuleParamDto> getRuleParams(RuleKey ruleKey) {
+ Set<RuleParamDto> res = ruleParams.get(requireNonNull(ruleKey, "RuleKey can't be null"));
+ return res == null ? Collections.emptySet() : res;
+ }
+ }
}
import org.sonar.db.qualityprofile.ActiveRuleParamDto;
import org.sonar.db.qualityprofile.QualityProfileDto;
import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleParamDto;
import static org.sonar.server.ws.WsUtils.checkRequest;
RuleActivatorContext create(String profileKey, RuleKey ruleKey, DbSession session) {
RuleActivatorContext context = new RuleActivatorContext();
- QualityProfileDto profile = db.qualityProfileDao().selectByKey(session, profileKey);
+ QualityProfileDto profile = getQualityProfileDto(session, profileKey);
checkRequest(profile != null, "Quality profile not found: %s", profileKey);
context.setProfile(profile);
return create(ruleKey, session, context);
private RuleDefinitionDto initRule(RuleKey ruleKey, RuleActivatorContext context, DbSession dbSession) {
Optional<RuleDefinitionDto> rule = getRule(dbSession, ruleKey);
checkRequest(rule.isPresent(), "Rule not found: %s", ruleKey);
- context.setRule(rule.get());
- context.setRuleParams(db.ruleDao().selectRuleParamsByRuleKey(dbSession, rule.get().getKey()));
- return rule.get();
+ RuleDefinitionDto ruleDefinitionDto = rule.get();
+ context.setRule(ruleDefinitionDto);
+ context.setRuleParams(getRuleParams(dbSession, ruleDefinitionDto));
+ return ruleDefinitionDto;
}
private void initActiveRules(String profileKey, RuleKey ruleKey, RuleActivatorContext context, DbSession session, boolean parent) {
}
}
+ QualityProfileDto getQualityProfileDto(DbSession session, String profileKey) {
+ return db.qualityProfileDao().selectByKey(session, profileKey);
+ }
+
Optional<RuleDefinitionDto> getRule(DbSession dbSession, RuleKey ruleKey) {
return Optional.ofNullable(db.ruleDao().selectDefinitionByKey(dbSession, ruleKey).orElse(null));
}
+ Collection<RuleParamDto> getRuleParams(DbSession dbSession, RuleDefinitionDto ruleDefinitionDto) {
+ return db.ruleDao().selectRuleParamsByRuleKey(dbSession, ruleDefinitionDto.getKey());
+ }
+
Optional<ActiveRuleDto> getActiveRule(DbSession session, ActiveRuleKey key) {
return Optional.ofNullable(db.activeRuleDao().selectByKey(session, key).orNull());
}
+++ /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.qualityprofile;
-
-import java.util.Arrays;
-import java.util.stream.Collectors;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.qualityprofile.QualityProfileDao;
-import org.sonar.db.qualityprofile.QualityProfileDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-public class CachingRuleActivatorTest {
- private DbClient dbClient = mock(DbClient.class);
- private DbSession dbSession = mock(DbSession.class);
- private QualityProfileDao qualityProfileDao = mock(QualityProfileDao.class);
- private CachingRuleActivator underTest = new CachingRuleActivator(null, dbClient, null, null, null, null, null);
-
- @Before
- public void wire_mocks() throws Exception {
- when(dbClient.openSession(anyBoolean())).thenReturn(dbSession);
- when(dbClient.qualityProfileDao()).thenReturn(qualityProfileDao);
- }
-
- @Test
- public void getChildren_caches_that_qp_has_no_children() {
- mockSelectChildrenForKey("no_children");
-
- assertThat(underTest.getChildren(dbSession, "no_children"))
- .isEmpty();
- assertThat(underTest.getChildren(dbSession, "no_children"))
- .isEmpty();
- assertThat(underTest.getChildren(dbSession, "no_children"))
- .isEmpty();
- verify(qualityProfileDao, times(1)).selectChildren(eq(dbSession), anyString());
- }
-
- @Test
- public void getChildren_caches_that_sq_has_one_or_more_children() {
- mockSelectChildrenForKey("0", "1");
- mockSelectChildrenForKey("1", "2", "3");
-
- assertThat(underTest.getChildren(dbSession, "0"))
- .extracting(QualityProfileDto::getKey)
- .containsExactly("1");
- assertThat(underTest.getChildren(dbSession, "0"))
- .extracting(QualityProfileDto::getKey)
- .containsExactly("1");
- assertThat(underTest.getChildren(dbSession, "0"))
- .extracting(QualityProfileDto::getKey)
- .containsExactly("1");
- assertThat(underTest.getChildren(dbSession, "1"))
- .extracting(QualityProfileDto::getKey)
- .containsExactly("2", "3");
- assertThat(underTest.getChildren(dbSession, "1"))
- .extracting(QualityProfileDto::getKey)
- .containsExactly("2", "3");
- assertThat(underTest.getChildren(dbSession, "1"))
- .extracting(QualityProfileDto::getKey)
- .containsExactly("2", "3");
- verify(qualityProfileDao, times(1)).selectChildren(dbSession, "0");
- verify(qualityProfileDao, times(1)).selectChildren(dbSession, "1");
- verifyNoMoreInteractions(qualityProfileDao);
- }
-
- private void mockSelectChildrenForKey(String key, String... children) {
- when(qualityProfileDao.selectChildren(dbSession, key))
- .thenReturn(Arrays.stream(children).map(this::dto).collect(Collectors.toList()))
- .thenThrow(new IllegalStateException("selectChildren should be called only once for key " + key));
- }
-
- private QualityProfileDto dto(String key) {
- return new QualityProfileDto() {
- @Override
- public String toString() {
- return getKey();
- }
- }.setKey(key);
- }
-}
private DbSession dbSession = dbClient.openSession(false);
private UuidFactory mockedUuidFactory = mock(UuidFactory.class);
private System2 mockedSystem2 = mock(System2.class);
- private CachingRuleActivator mockedCachingRuleActivator = mock(CachingRuleActivator.class);
+ private RuleActivator mockedRuleActivator = mock(RuleActivator.class);
private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
private DefinedQProfileCreationImpl underTest = new DefinedQProfileCreationImpl(
dbClient,
new QProfileFactory(dbClient, mockedUuidFactory, mockedSystem2, activeRuleIndexer),
- mockedCachingRuleActivator);
+ mockedRuleActivator);
private List<ActiveRuleChange> activeRuleChanges = new ArrayList<>();
@After
String uuid = "uuid 1";
mockForSingleQPInsert(uuid, date);
QualityProfileDto existing = dbTester.qualityProfiles().insertQualityProfile(
- QualityProfileDto.createFor("a key")
- .setName(definedQProfile.getName())
- .setLanguage(definedQProfile.getLanguage())
- .setOrganizationUuid(organization.getUuid()));
+ QualityProfileDto.createFor("a key")
+ .setName(definedQProfile.getName())
+ .setLanguage(definedQProfile.getLanguage())
+ .setOrganizationUuid(organization.getUuid()));
underTest.create(dbSession, definedQProfile, organization, activeRuleChanges);
dbSession.commit();
QualityProfileDto qualityProfileDto = t.getArgumentAt(2, QualityProfileDto.class);
callLogs.add(new CallLog(ruleActivation, qualityProfileDto));
return changesPerCallIt.next();
- }).when(mockedCachingRuleActivator)
+ }).when(mockedRuleActivator)
.activate(any(DbSession.class), any(RuleActivation.class), any(QualityProfileDto.class));
}
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
+import java.util.stream.IntStream;
+import javax.annotation.Nullable;
+import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.server.tester.UserSessionRule;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
private DbClient dbClient = dbTester.getDbClient();
private DbClient mockedDbClient = mock(DbClient.class);
private ActiveRuleIndexer mockedActiveRuleIndexer = mock(ActiveRuleIndexer.class);
- private DummyDefinedQProfileCreation definedQProfileCreation = new DummyDefinedQProfileCreation();
+ private DummyDefinedQProfileInsert definedQProfileInsert = new DummyDefinedQProfileInsert();
private RegisterQualityProfiles underTest = new RegisterQualityProfiles(
definedQProfileRepositoryRule,
dbClient,
- definedQProfileCreation,
+ definedQProfileInsert,
mockedActiveRuleIndexer);
@Test
underTest.start();
- assertThat(definedQProfileCreation.getCallLogs()).isEmpty();
- verify(mockedDbClient).openSession(false);
- verify(mockedActiveRuleIndexer).index(Collections.emptyList());
+ assertThat(definedQProfileInsert.getCallLogs()).isEmpty();
+ verify(mockedDbClient, times(0)).openSession(anyBoolean());
+ verify(mockedActiveRuleIndexer, times(0)).index(anyList());
verifyNoMoreInteractions(mockedDbClient, mockedActiveRuleIndexer);
}
underTest.start();
- assertThat(definedQProfileCreation.getCallLogs())
+ assertThat(definedQProfileInsert.getCallLogs())
.containsExactly(
callLog(definedQProfile, dbTester.getDefaultOrganization()),
callLog(definedQProfile, organization1),
underTest.start();
- assertThat(definedQProfileCreation.getCallLogs())
+ assertThat(definedQProfileInsert.getCallLogs())
.containsExactly(callLog(definedQProfile, org2));
}
underTest.start();
- assertThat(definedQProfileCreation.getCallLogs())
+ assertThat(definedQProfileInsert.getCallLogs())
.containsExactly(callLog(definedQProfile2, dbTester.getDefaultOrganization()), callLog(definedQProfile1, dbTester.getDefaultOrganization()));
}
ActiveRuleChange ruleChange2 = newActiveRuleChange("2");
ActiveRuleChange ruleChange3 = newActiveRuleChange("3");
ActiveRuleChange ruleChange4 = newActiveRuleChange("4");
- definedQProfileCreation.addChangesPerCall(ruleChange1, ruleChange3);
+ definedQProfileInsert.addChangesPerCall(ruleChange1, ruleChange3);
// no change for second org
- definedQProfileCreation.addChangesPerCall();
- definedQProfileCreation.addChangesPerCall(ruleChange2);
- definedQProfileCreation.addChangesPerCall(ruleChange4);
+ definedQProfileInsert.addChangesPerCall();
+ definedQProfileInsert.addChangesPerCall(ruleChange2);
+ definedQProfileInsert.addChangesPerCall(ruleChange4);
ArgumentCaptor<List<ActiveRuleChange>> indexedChangesCaptor = ArgumentCaptor.forClass((Class<List<ActiveRuleChange>>) (Object) List.class);
doNothing().when(mockedActiveRuleIndexer).index(indexedChangesCaptor.capture());
.containsExactly(ruleChange1, ruleChange3, ruleChange2, ruleChange4);
}
+ @Test
+ public void test_SortByParentName_comporator() {
+ DefinedQProfile[] builderArray = {newBuilder("A1", null), newBuilder("A2", "A1"), newBuilder("A3", null), newBuilder("A4", "A3"),
+ newBuilder("A5", "A4"), newBuilder("A6", null)};
+ List<DefinedQProfile> builders = new ArrayList<>(Arrays.asList(builderArray));
+
+ IntStream.range(0, 100)
+ .forEach(i -> {
+ Collections.shuffle(builders);
+ RegisterQualityProfiles.SortByParentName comparator = new RegisterQualityProfiles.SortByParentName(builders);
+
+ assertThat(comparator.depthByBuilder.get("A1")).isEqualTo(0);
+ assertThat(comparator.depthByBuilder.get("A2")).isEqualTo(1);
+ assertThat(comparator.depthByBuilder.get("A3")).isEqualTo(0);
+ assertThat(comparator.depthByBuilder.get("A4")).isEqualTo(1);
+ assertThat(comparator.depthByBuilder.get("A5")).isEqualTo(2);
+ assertThat(comparator.depthByBuilder.get("A6")).isEqualTo(0);
+
+ builders.sort(comparator);
+
+ verifyParentBeforeChild(builderArray, builders, 0, 1);
+ verifyParentBeforeChild(builderArray, builders, 2, 3);
+ verifyParentBeforeChild(builderArray, builders, 3, 4);
+ verifyParentBeforeChild(builderArray, builders, 2, 4);
+ });
+ }
+
+ private DefinedQProfile newBuilder(String name, @Nullable String parentName) {
+ return new DefinedQProfile.Builder()
+ .setName(name)
+ .setParentName(parentName)
+ .build(DigestUtils.getMd5Digest());
+ }
+
+ private static void verifyParentBeforeChild(DefinedQProfile[] builderArray, List<DefinedQProfile> builders,
+ int parent, int child) {
+ assertThat(builders.indexOf(builderArray[parent]))
+ .describedAs(builderArray[4].getName() + " before " + builderArray[child].getName())
+ .isLessThan(builders.indexOf(builderArray[child]));
+ }
+
private static ActiveRuleChange newActiveRuleChange(String id) {
return ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(id, RuleKey.of(id + "1", id + "2")));
}
- private class DummyDefinedQProfileCreation implements DefinedQProfileCreation {
+ private class DummyDefinedQProfileInsert implements DefinedQProfileInsert {
private List<List<ActiveRuleChange>> changesPerCall;
private Iterator<List<ActiveRuleChange>> changesPerCallIterator;
private final List<CallLog> callLogs = new ArrayList<>();