Browse Source

SONAR-15237 Improve startup performance of the web process

tags/9.1.0.47736
Duarte Meneses 2 years ago
parent
commit
bc4d1696a5
28 changed files with 646 additions and 260 deletions
  1. 4
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java
  2. 2
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java
  3. 2
    2
      server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
  4. 16
    5
      server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml
  5. 20
    10
      server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java
  6. 6
    0
      server/sonar-server-common/src/main/java/org/sonar/server/rule/ServerRuleFinder.java
  7. 21
    8
      server/sonar-server-common/src/test/java/org/sonar/server/rule/DefaultRuleFinderTest.java
  8. 108
    0
      server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/DetectPluginChange.java
  9. 23
    18
      server/sonar-webserver-api/src/main/java/org/sonar/server/rule/CachingRuleFinder.java
  10. 0
    0
      server/sonar-webserver-api/src/main/java/org/sonar/server/rule/WebServerRuleFinder.java
  11. 11
    0
      server/sonar-webserver-api/src/main/java/org/sonar/server/rule/WebServerRuleFinderImpl.java
  12. 116
    0
      server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/DetectPluginChangeTest.java
  13. 3
    8
      server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierImpl.java
  14. 32
    39
      server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java
  15. 7
    4
      server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java
  16. 5
    10
      server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierImplTest.java
  17. 66
    11
      server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java
  18. 28
    26
      server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java
  19. 99
    81
      server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java
  20. 4
    4
      server/sonar-webserver-core/src/main/java/org/sonar/server/startup/RegisterMetrics.java
  21. 2
    1
      server/sonar-webserver-core/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java
  22. 9
    7
      server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
  23. 3
    7
      server/sonar-webserver-core/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java
  24. 3
    4
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java
  25. 7
    2
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java
  26. 3
    3
      server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java
  27. 4
    1
      server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel3.java
  28. 42
    9
      server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java

+ 4
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java View File

@@ -203,6 +203,10 @@ public class RuleDao implements Dao {
return executeLargeInputs(ruleKeys, mapper(session)::selectParamsByRuleKeys);
}

public List<RuleParamDto> selectAllRuleParams(DbSession session) {
return mapper(session).selectAllRuleParams();
}

public List<RuleParamDto> selectRuleParamsByRuleUuids(DbSession dbSession, Collection<String> ruleUuids) {
return executeLargeInputs(ruleUuids, mapper(dbSession)::selectParamsByRuleUuids);
}

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java View File

@@ -81,6 +81,8 @@ public interface RuleMapper {

List<RuleParamDto> selectParamsByRuleKeys(@Param("ruleKeys") List<RuleKey> ruleKeys);

List<RuleParamDto> selectAllRuleParams();

void insertParameter(RuleParamDto param);

void updateParameter(RuleParamDto param);

+ 2
- 2
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml View File

@@ -36,7 +36,7 @@
inner join rules r on r.uuid = a.rule_uuid
</sql>

<insert id="insert" parameterType="ActiveRule">
<insert id="insert" parameterType="ActiveRule" useGeneratedKeys="false">
insert into active_rules (
uuid,
profile_uuid,
@@ -56,7 +56,7 @@
)
</insert>

<update id="update" parameterType="ActiveRule">
<update id="update" parameterType="ActiveRule" useGeneratedKeys="false">
update active_rules
set
failure_level = #{severity, jdbcType=INTEGER},

+ 16
- 5
server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml View File

@@ -148,7 +148,7 @@
</select>

<select id="selectIndexingRuleExtensionsByIds" parameterType="map" resultType="org.sonar.db.rule.RuleExtensionForIndexingDto">
<include refid="sqlSelectIndexingRuleExtensions" />
<include refid="sqlSelectIndexingRuleExtensions"/>
and
<foreach collection="ruleExtensionIds" index="index" item="ruleExtId" open="" separator=" or " close="">
( r.uuid = #{ruleExtId, jdbcType=VARCHAR} )
@@ -263,7 +263,8 @@
order by r.created_at asc
</select>

<select id="scrollIndexingRules" resultType="org.sonar.db.rule.RuleForIndexingDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
<select id="scrollIndexingRules" resultType="org.sonar.db.rule.RuleForIndexingDto" fetchSize="${_scrollFetchSize}"
resultSetType="FORWARD_ONLY">
<include refid="sqlSelectIndexingRules"/>
order by r.created_at asc
</select>
@@ -324,8 +325,10 @@
<include refid="outerJoinRulesMetadata"/>
where
r.status != 'REMOVED' and r.is_external=${_false} and r.is_template=${_false}
and r.rule_type in <foreach collection="types" item="type" separator="," open="(" close=")">#{type, jdbcType=INTEGER}</foreach>
and r.language in <foreach collection="languages" item="language" separator="," open="(" close=")">#{language, jdbcType=VARCHAR}</foreach>
and r.rule_type in
<foreach collection="types" item="type" separator="," open="(" close=")">#{type, jdbcType=INTEGER}</foreach>
and r.language in
<foreach collection="languages" item="language" separator="," open="(" close=")">#{language, jdbcType=VARCHAR}</foreach>
</select>

<insert id="insertDefinition" parameterType="org.sonar.db.rule.RuleDefinitionDto" useGeneratedKeys="false">
@@ -506,6 +509,13 @@
</foreach>
</select>

<select id="selectAllRuleParams" resultType="RuleParam">
select
<include refid="paramColumns"/>
from
rules_parameters p
</select>

<select id="selectParamsByRuleKey" resultType="RuleParam" parameterType="org.sonar.api.rule.RuleKey">
select
<include refid="paramColumns"/>
@@ -606,7 +616,8 @@
</foreach>
</delete>

<insert id="insertDeprecatedRuleKey" parameterType="org.sonar.db.rule.DeprecatedRuleKeyDto" keyColumn="uuid" useGeneratedKeys="false" keyProperty="uuid">
<insert id="insertDeprecatedRuleKey" parameterType="org.sonar.db.rule.DeprecatedRuleKeyDto" keyColumn="uuid" useGeneratedKeys="false"
keyProperty="uuid">
INSERT INTO deprecated_rule_keys (
uuid,
rule_uuid,

+ 20
- 10
server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java View File

@@ -42,9 +42,6 @@ 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 java.util.Optional.empty;

/**
* Will be removed in the future.
*/
@@ -61,12 +58,25 @@ public class DefaultRuleFinder implements ServerRuleFinder {
@Override
public Optional<RuleDefinitionDto> findDtoByKey(RuleKey key) {
try (DbSession dbSession = dbClient.openSession(false)) {
Optional<RuleDefinitionDto> rule = ruleDao.selectDefinitionByKey(dbSession, key);
if (rule.isPresent() && rule.get().getStatus() != RuleStatus.REMOVED) {
return rule;
} else {
return empty();
}
return ruleDao.selectDefinitionByKey(dbSession, key)
.filter(r -> r.getStatus() != RuleStatus.REMOVED);
}
}

@Override
public Optional<RuleDefinitionDto> findDtoByUuid(String uuid) {
try (DbSession dbSession = dbClient.openSession(false)) {
return ruleDao.selectDefinitionByUuid(uuid, dbSession)
.filter(r -> r.getStatus() != RuleStatus.REMOVED);
}
}

@Override
public Collection<RuleDefinitionDto> findAll() {
try (DbSession dbSession = dbClient.openSession(false)) {
List<RuleDefinitionDto> list = new ArrayList<>();
ruleDao.selectEnabled(dbSession, r -> list.add(r.getResultObject()));
return list;
}
}

@@ -150,7 +160,7 @@ public class DefaultRuleFinder implements ServerRuleFinder {
}
}

List<org.sonar.api.rules.RuleParam> apiParams = newArrayList();
List<org.sonar.api.rules.RuleParam> apiParams = new ArrayList<>();
for (RuleParamDto param : params) {
apiParams.add(new org.sonar.api.rules.RuleParam(apiRule, param.getName(), param.getDescription(), param.getType())
.setDefaultValue(param.getDefaultValue()));

+ 6
- 0
server/sonar-server-common/src/main/java/org/sonar/server/rule/ServerRuleFinder.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.server.rule;

import java.util.Collection;
import java.util.Optional;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleFinder;
@@ -26,4 +27,9 @@ import org.sonar.db.rule.RuleDefinitionDto;

public interface ServerRuleFinder extends RuleFinder {
Optional<RuleDefinitionDto> findDtoByKey(RuleKey key);

Optional<RuleDefinitionDto> findDtoByUuid(String uuid);

Collection<RuleDefinitionDto> findAll();

}

+ 21
- 8
server/sonar-server-common/src/test/java/org/sonar/server/rule/DefaultRuleFinderTest.java View File

@@ -22,6 +22,7 @@ package org.sonar.server.rule;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleQuery;
@@ -41,10 +42,10 @@ public class DefaultRuleFinderTest {
@org.junit.Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);

private DbClient dbClient = dbTester.getDbClient();
private DbSession session = dbTester.getSession();
private final DbClient dbClient = dbTester.getDbClient();
private final DbSession session = dbTester.getSession();

private RuleDto rule1 = new RuleDto()
private final RuleDto rule1 = new RuleDto()
.setName("Check Header")
.setConfigKey("Checker/Treewalker/HeaderCheck")
.setRuleKey("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck")
@@ -53,7 +54,7 @@ public class DefaultRuleFinderTest {
.setScope(Scope.MAIN)
.setStatus(RuleStatus.READY);

private RuleDto rule2 = new RuleDto()
private final RuleDto rule2 = new RuleDto()
.setName("Disabled checked")
.setConfigKey("Checker/Treewalker/DisabledCheck")
.setRuleKey("DisabledCheck")
@@ -62,7 +63,7 @@ public class DefaultRuleFinderTest {
.setScope(Scope.MAIN)
.setStatus(RuleStatus.REMOVED);

private RuleDto rule3 = new RuleDto()
private final RuleDto rule3 = new RuleDto()
.setName("Check Annotation")
.setConfigKey("Checker/Treewalker/AnnotationUseStyleCheck")
.setRuleKey("com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck")
@@ -71,7 +72,7 @@ public class DefaultRuleFinderTest {
.setScope(Scope.MAIN)
.setStatus(RuleStatus.READY);

private RuleDto rule4 = new RuleDto()
private final RuleDto rule4 = new RuleDto()
.setName("Call Super First")
.setConfigKey("rulesets/android.xml/CallSuperFirst")
.setRuleKey("CallSuperFirst")
@@ -80,7 +81,7 @@ public class DefaultRuleFinderTest {
.setScope(Scope.MAIN)
.setStatus(RuleStatus.READY);

private DefaultRuleFinder underTest = new DefaultRuleFinder(dbClient);
private final DefaultRuleFinder underTest = new DefaultRuleFinder(dbClient);

@Before
public void setup() {
@@ -108,19 +109,31 @@ public class DefaultRuleFinderTest {

// find_all_enabled
assertThat(underTest.findAll(RuleQuery.create())).extracting("ruleKey").containsOnly(rule1.getKey(), rule3.getKey(), rule4.getKey());
assertThat(underTest.findAll(RuleQuery.create())).hasSize(3);

// find_all
assertThat(underTest.findAll()).extracting("ruleKey").containsOnly(rule1.getKey().rule(), rule3.getKey().rule(), rule4.getKey().rule());

// do_not_find_disabled_rules
assertThat(underTest.findByKey("checkstyle", "DisabledCheck")).isNull();

// do_not_find_unknown_rules
assertThat(underTest.findAll(RuleQuery.create().withRepositoryKey("unknown_repository"))).isEmpty();

assertThat(underTest.findDtoByKey(RuleKey.of("pmd", "CallSuperFirst")).get().getUuid()).isEqualTo(rule4.getUuid());
assertThat(underTest.findDtoByUuid(rule4.getUuid())).isPresent();
}

@Test
public void should_fail_find() {
assertThat(underTest.findDtoByKey(RuleKey.of("pmd", "unknown"))).isEmpty();
assertThat(underTest.findDtoByUuid("unknown")).isEmpty();
}

@Test
public void find_all_not_include_removed_rule() {
// rule 3 is REMOVED
assertThat(underTest.findAll(RuleQuery.create())).extracting("ruleKey").containsOnly(rule1.getKey(), rule3.getKey(), rule4.getKey());
assertThat(underTest.findAll()).extracting("ruleKey").containsOnly(rule1.getKey().rule(), rule3.getKey().rule(), rule4.getKey().rule());
}

@Test

+ 108
- 0
server/sonar-webserver-api/src/main/java/org/sonar/server/plugins/DetectPluginChange.java View File

@@ -0,0 +1,108 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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.plugins;

import java.util.Map;
import java.util.stream.Collectors;
import org.sonar.api.Startable;
import org.sonar.api.utils.Preconditions;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.plugin.PluginDto;

import static java.util.function.Function.identity;

public class DetectPluginChange implements Startable {
private static final Logger LOG = Loggers.get(DetectPluginChange.class);

private final ServerPluginRepository serverPluginRepository;
private final DbClient dbClient;
private Boolean changesDetected = null;

public DetectPluginChange(ServerPluginRepository serverPluginRepository, DbClient dbClient) {
this.serverPluginRepository = serverPluginRepository;
this.dbClient = dbClient;
}

@Override
public void start() {
Preconditions.checkState(changesDetected == null, "Can only call #start() once");
Profiler profiler = Profiler.create(LOG).startInfo("Detect plugin changes");
changesDetected = anyChange();
if (changesDetected) {
LOG.info("Plugin changes detected");
} else {
LOG.debug("No plugin changes detected");
}
profiler.stopDebug();
}

/**
* @throws NullPointerException if {@link #start} hasn't been called
*/
public boolean anyPluginChanged() {
return changesDetected;
}

private boolean anyChange() {
try (DbSession dbSession = dbClient.openSession(false)) {
Map<String, PluginDto> dbPluginsByKey = dbClient.pluginDao().selectAll(dbSession).stream()
.collect(Collectors.toMap(PluginDto::getKee, identity()));
Map<String, ServerPlugin> filePluginsByKey = serverPluginRepository.getPlugins().stream()
.collect(Collectors.toMap(p -> p.getPluginInfo().getKey(), p -> p));

if (!dbPluginsByKey.keySet().equals(filePluginsByKey.keySet())) {
return true;
}

// TODO ideally we would detect plugins that were removed and added again, because we don't delete removed plugins from the plugins table.
for (ServerPlugin installed : filePluginsByKey.values()) {
PluginDto dbPlugin = dbPluginsByKey.get(installed.getPluginInfo().getKey());
if (changed(dbPlugin, installed)) {
return true;
}
}
}
return false;
}

private boolean changed(PluginDto dbPlugin, ServerPlugin filePlugin) {
return !dbPlugin.getFileHash().equals(filePlugin.getJar().getMd5()) || !dbPlugin.getType().equals(toTypeDto(filePlugin.getType()));
}

static PluginDto.Type toTypeDto(PluginType type) {
switch (type) {
case EXTERNAL:
return PluginDto.Type.EXTERNAL;
case BUNDLED:
return PluginDto.Type.BUNDLED;
default:
throw new IllegalStateException("Unknown type: " + type);
}
}

@Override
public void stop() {
// Nothing to do
}
}

server/sonar-webserver-core/src/main/java/org/sonar/server/rule/CachingRuleFinder.java → server/sonar-webserver-api/src/main/java/org/sonar/server/rule/CachingRuleFinder.java View File

@@ -19,10 +19,8 @@
*/
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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -30,7 +28,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -48,8 +45,8 @@ 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 java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableMap;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;

/**
@@ -61,6 +58,7 @@ public class CachingRuleFinder implements ServerRuleFinder {
private static final Ordering<Map.Entry<RuleDefinitionDto, Rule>> FIND_BY_QUERY_ORDER = Ordering.natural().reverse().onResultOf(entry -> entry.getKey().getUpdatedAt());

private final Map<RuleKey, RuleDefinitionDto> ruleDtosByKey;
private final Map<String, RuleDefinitionDto> ruleDtosByUuid;
private final Map<RuleDefinitionDto, Rule> rulesByRuleDefinition;
private final Map<RuleKey, Rule> rulesByKey;

@@ -68,6 +66,7 @@ public class CachingRuleFinder implements ServerRuleFinder {
try (DbSession dbSession = dbClient.openSession(false)) {
List<RuleDefinitionDto> dtos = dbClient.ruleDao().selectAllDefinitions(dbSession);
this.ruleDtosByKey = dtos.stream().collect(Collectors.toMap(RuleDefinitionDto::getKey, d -> d));
this.ruleDtosByUuid = dtos.stream().collect(Collectors.toMap(RuleDefinitionDto::getUuid, d -> d));
this.rulesByRuleDefinition = buildRulesByRuleDefinitionDto(dbClient, dbSession, dtos);
this.rulesByKey = this.rulesByRuleDefinition.entrySet().stream()
.collect(uniqueIndex(entry -> entry.getKey().getKey(), Map.Entry::getValue));
@@ -75,22 +74,17 @@ public class CachingRuleFinder implements ServerRuleFinder {
}

private static Map<RuleDefinitionDto, Rule> buildRulesByRuleDefinitionDto(DbClient dbClient, DbSession dbSession, List<RuleDefinitionDto> dtos) {
Set<RuleKey> ruleKeys = dtos.stream().map(RuleDefinitionDto::getKey).collect(toSet(dtos.size()));
ListMultimap<String, RuleParamDto> ruleParamsByRuleUuid = retrieveRuleParameters(dbClient, dbSession, ruleKeys);
Map<String, List<RuleParamDto>> ruleParamsByRuleUuid = retrieveRuleParameters(dbClient, dbSession);
Map<RuleDefinitionDto, Rule> rulesByDefinition = new HashMap<>(dtos.size());
for (RuleDefinitionDto definition : dtos) {
rulesByDefinition.put(definition, toRule(definition, ruleParamsByRuleUuid.get(definition.getUuid())));
rulesByDefinition.put(definition, toRule(definition, ruleParamsByRuleUuid.getOrDefault(definition.getUuid(), emptyList())));
}
return ImmutableMap.copyOf(rulesByDefinition);
return unmodifiableMap(rulesByDefinition);
}

private static ImmutableListMultimap<String, 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::getRuleUuid));
private static Map<String, List<RuleParamDto>> retrieveRuleParameters(DbClient dbClient, DbSession dbSession) {
return dbClient.ruleDao().selectAllRuleParams(dbSession).stream()
.collect(Collectors.groupingBy(RuleParamDto::getRuleUuid));
}

@Override
@@ -178,7 +172,7 @@ public class CachingRuleFinder implements ServerRuleFinder {
}
}

List<org.sonar.api.rules.RuleParam> apiParams = newArrayList();
List<org.sonar.api.rules.RuleParam> apiParams = new ArrayList<>();
for (RuleParamDto param : params) {
apiParams.add(new org.sonar.api.rules.RuleParam(apiRule, param.getName(), param.getDescription(), param.getType())
.setDefaultValue(param.getDefaultValue()));
@@ -192,4 +186,15 @@ public class CachingRuleFinder implements ServerRuleFinder {
public Optional<RuleDefinitionDto> findDtoByKey(RuleKey key) {
return Optional.ofNullable(this.ruleDtosByKey.get(key));
}

@Override
public Optional<RuleDefinitionDto> findDtoByUuid(String uuid) {
return Optional.ofNullable(this.ruleDtosByUuid.get(uuid));
}

@Override
public Collection<RuleDefinitionDto> findAll() {
return ruleDtosByUuid.values();
}

}

server/sonar-webserver-core/src/main/java/org/sonar/server/rule/WebServerRuleFinder.java → server/sonar-webserver-api/src/main/java/org/sonar/server/rule/WebServerRuleFinder.java View File


server/sonar-webserver-core/src/main/java/org/sonar/server/rule/WebServerRuleFinderImpl.java → server/sonar-webserver-api/src/main/java/org/sonar/server/rule/WebServerRuleFinderImpl.java View File

@@ -78,4 +78,15 @@ public class WebServerRuleFinderImpl implements WebServerRuleFinder {
public Optional<RuleDefinitionDto> findDtoByKey(RuleKey key) {
return delegate.findDtoByKey(key);
}

@Override
public Optional<RuleDefinitionDto> findDtoByUuid(String uuid) {
return delegate.findDtoByUuid(uuid);
}

@Override
public Collection<RuleDefinitionDto> findAll() {
return delegate.findAll();
}

}

+ 116
- 0
server/sonar-webserver-api/src/test/java/org/sonar/server/plugins/DetectPluginChangeTest.java View File

@@ -0,0 +1,116 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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.plugins;

import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.Plugin;
import org.sonar.core.platform.PluginInfo;
import org.sonar.db.DbTester;
import org.sonar.db.plugin.PluginDto;
import org.sonar.server.plugins.PluginFilesAndMd5.FileAndMd5;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class DetectPluginChangeTest {
@Rule
public DbTester dbTester = DbTester.create();

private final ServerPluginRepository pluginRepository = new ServerPluginRepository();
private final DetectPluginChange detectPluginChange = new DetectPluginChange(pluginRepository, dbTester.getDbClient());

@Test
public void detect_changed_plugin() {
addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
addPluginToFs("plugin1", "hash2", PluginType.BUNDLED);

detectPluginChange.start();
assertThat(detectPluginChange.anyPluginChanged()).isTrue();
}

@Test
public void detect_changed_type() {
addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
addPluginToFs("plugin1", "hash1", PluginType.EXTERNAL);

detectPluginChange.start();
assertThat(detectPluginChange.anyPluginChanged()).isTrue();
}

@Test
public void detect_new_plugin() {
addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
addPluginToFs("plugin2", "hash1", PluginType.BUNDLED);

detectPluginChange.start();
assertThat(detectPluginChange.anyPluginChanged()).isTrue();
}

@Ignore
@Test
public void detect_missing_plugin() {
// TODO
addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
addPluginToFs("plugin1", "hash1", PluginType.BUNDLED);

detectPluginChange.start();
assertThat(detectPluginChange.anyPluginChanged()).isTrue();
}

@Test
public void detect_no_changes_bundled() {
addPluginToDb("plugin1", "hash1", PluginDto.Type.BUNDLED);
addPluginToFs("plugin1", "hash1", PluginType.BUNDLED);
addPluginToDb("plugin2", "hash2", PluginDto.Type.EXTERNAL);
addPluginToFs("plugin2", "hash2", PluginType.EXTERNAL);

detectPluginChange.start();
assertThat(detectPluginChange.anyPluginChanged()).isFalse();
}

@Test
public void fail_if_start_twice() {
detectPluginChange.start();
assertThrows(IllegalStateException.class, detectPluginChange::start);
}

@Test
public void fail_if_not_started() {
assertThrows(NullPointerException.class, detectPluginChange::anyPluginChanged);
}

private void addPluginToDb(String key, String hash, PluginDto.Type type) {
dbTester.pluginDbTester().insertPlugin(p -> p.setKee(key).setFileHash(hash).setType(type));
}

private void addPluginToFs(String key, String hash, PluginType type) {
PluginInfo pluginInfo = new PluginInfo(key);
Plugin plugin = mock(Plugin.class);
FileAndMd5 fileAndMd5 = mock(FileAndMd5.class);
when(fileAndMd5.getMd5()).thenReturn(hash);
ServerPlugin serverPlugin = new ServerPlugin(pluginInfo, type, plugin, fileAndMd5, null);
pluginRepository.addPlugin(serverPlugin);
}

}

+ 3
- 8
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierImpl.java View File

@@ -35,7 +35,7 @@ import static org.sonar.server.property.InternalProperties.DEFAULT_ADMIN_CREDENT
/**
* Detect usage of an active admin account with default credential in order to ask this account to reset its password during authentication.
*/
public class DefaultAdminCredentialsVerifierImpl implements Startable, DefaultAdminCredentialsVerifier {
public class DefaultAdminCredentialsVerifierImpl implements DefaultAdminCredentialsVerifier {

private static final Logger LOGGER = Loggers.get(STARTUP_LOGGER_NAME);

@@ -49,8 +49,7 @@ public class DefaultAdminCredentialsVerifierImpl implements Startable, DefaultAd
this.notificationManager = notificationManager;
}

@Override
public void start() {
public void runAtStart() {
try (DbSession session = dbClient.openSession(false)) {
UserDto admin = getAdminUser(session);
if (admin == null || !isDefaultCredentialUser(session, admin)) {
@@ -63,6 +62,7 @@ public class DefaultAdminCredentialsVerifierImpl implements Startable, DefaultAd
}
}

@Override
public boolean hasDefaultCredentialUser() {
try (DbSession session = dbClient.openSession(false)) {
UserDto admin = getAdminUser(session);
@@ -105,9 +105,4 @@ public class DefaultAdminCredentialsVerifierImpl implements Startable, DefaultAd
notificationManager.scheduleForSending(new DefaultAdminCredentialsVerifierNotification());
dbClient.internalPropertiesDao().save(session, DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL, Boolean.TRUE.toString());
}

@Override
public void stop() {
// Nothing to do
}
}

+ 32
- 39
server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImpl.java View File

@@ -20,17 +20,14 @@
package org.sonar.server.qualityprofile;

import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -51,22 +48,27 @@ import org.sonar.db.qualityprofile.RulesProfileDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleParamDto;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.ServerRuleFinder;
import org.sonar.server.util.TypeValidations;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.emptySet;
import static java.util.Objects.requireNonNull;

public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
private final DbClient dbClient;
private final ServerRuleFinder ruleFinder;
private final System2 system2;
private final UuidFactory uuidFactory;
private final TypeValidations typeValidations;
private final ActiveRuleIndexer activeRuleIndexer;
private RuleRepository ruleRepository;

public BuiltInQProfileInsertImpl(DbClient dbClient, System2 system2, UuidFactory uuidFactory, TypeValidations typeValidations, ActiveRuleIndexer activeRuleIndexer) {
public BuiltInQProfileInsertImpl(DbClient dbClient, ServerRuleFinder ruleFinder, System2 system2, UuidFactory uuidFactory,
TypeValidations typeValidations, ActiveRuleIndexer activeRuleIndexer) {
this.dbClient = dbClient;
this.ruleFinder = ruleFinder;
this.system2 = system2;
this.uuidFactory = uuidFactory;
this.typeValidations = typeValidations;
@@ -80,9 +82,8 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
Date now = new Date(system2.now());
RulesProfileDto ruleProfile = insertRulesProfile(dbSession, builtInQProfile, now);

List<ActiveRuleChange> changes = builtInQProfile.getActiveRules()
.stream()
.map(activeRule -> insertActiveRule(dbSession, ruleProfile, activeRule, now.getTime()))
List<ActiveRuleChange> changes = builtInQProfile.getActiveRules().stream()
.map(activeRule -> insertActiveRule(dbSession, batchDbSession, ruleProfile, activeRule, now.getTime()))
.collect(MoreCollectors.toList());

changes.forEach(change -> dbClient.qProfileChangeDao().insert(batchDbSession, change.toDto(null)));
@@ -95,6 +96,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
activeRuleIndexer.commitAndIndex(dbSession, changes);
}


private void createDefaultAndOrgQProfiles(DbSession dbSession, DbSession batchDbSession, BuiltInQProfile builtIn, RulesProfileDto rulesProfileDto) {
Optional<String> qProfileUuid = dbClient.defaultQProfileDao().selectDefaultQProfileUuid(dbSession, builtIn.getLanguage());

@@ -102,7 +104,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
.setRulesProfileUuid(rulesProfileDto.getUuid())
.setUuid(uuidFactory.create());

if (builtIn.isDefault() && !qProfileUuid.isPresent()) {
if (builtIn.isDefault() && qProfileUuid.isEmpty()) {
DefaultQProfileDto defaultQProfileDto = new DefaultQProfileDto()
.setQProfileUuid(dto.getUuid())
.setLanguage(builtIn.getLanguage());
@@ -114,7 +116,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {

private void initRuleRepository(DbSession dbSession) {
if (ruleRepository == null) {
ruleRepository = new RuleRepository(dbClient, dbSession);
ruleRepository = new RuleRepository(dbClient, dbSession, ruleFinder);
}
}

@@ -129,7 +131,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
return dto;
}

private ActiveRuleChange insertActiveRule(DbSession dbSession, RulesProfileDto rulesProfileDto, BuiltInQProfile.ActiveRule activeRule, long now) {
private ActiveRuleChange insertActiveRule(DbSession dbSession, DbSession batchDbSession, RulesProfileDto rulesProfileDto, BuiltInQProfile.ActiveRule activeRule, long now) {
RuleKey ruleKey = activeRule.getRuleKey();
RuleDefinitionDto ruleDefinitionDto = ruleRepository.getDefinition(ruleKey)
.orElseThrow(() -> new IllegalStateException("RuleDefinition not found for key " + ruleKey));
@@ -141,7 +143,7 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
dto.setSeverity(firstNonNull(activeRule.getSeverity(), ruleDefinitionDto.getSeverityString()));
dto.setUpdatedAt(now);
dto.setCreatedAt(now);
dbClient.activeRuleDao().insert(dbSession, dto);
dbClient.activeRuleDao().insert(batchDbSession, dto);

List<ActiveRuleParamDto> paramDtos = insertActiveRuleParams(dbSession, activeRule, dto);

@@ -151,13 +153,10 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
return change;
}

private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, BuiltInQProfile.ActiveRule activeRule,
ActiveRuleDto activeRuleDto) {
Map<String, String> valuesByParamKey = activeRule.getParams()
.stream()
private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, BuiltInQProfile.ActiveRule activeRule, ActiveRuleDto activeRuleDto) {
Map<String, String> valuesByParamKey = activeRule.getParams().stream()
.collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue));
List<ActiveRuleParamDto> rules = ruleRepository.getRuleParams(activeRule.getRuleKey())
.stream()
List<ActiveRuleParamDto> rules = ruleRepository.getRuleParams(activeRule.getRuleKey()).stream()
.map(param -> createParamDto(param, Optional.ofNullable(valuesByParamKey.get(param.getName())).orElse(param.getDefaultValue())))
.filter(Objects::nonNull)
.collect(Collectors.toList());
@@ -188,35 +187,29 @@ public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
}

private static class RuleRepository {
private final Map<RuleKey, RuleDefinitionDto> definitions;
private final Map<RuleKey, Set<RuleParamDto>> params;
private final ServerRuleFinder ruleFinder;

private RuleRepository(DbClient dbClient, DbSession session, ServerRuleFinder ruleFinder) {
this.ruleFinder = ruleFinder;
this.params = new HashMap<>();

for (RuleParamDto ruleParam : dbClient.ruleDao().selectAllRuleParams(session)) {
Optional<RuleKey> ruleKey = ruleFinder.findDtoByUuid(ruleParam.getRuleUuid())
.map(r -> RuleKey.of(r.getRepositoryKey(), r.getRuleKey()));

private RuleRepository(DbClient dbClient, DbSession session) {
this.definitions = dbClient.ruleDao().selectAllDefinitions(session)
.stream()
.collect(Collectors.toMap(RuleDefinitionDto::getKey, Function.identity()));
Map<String, RuleKey> ruleUuidsByKey = definitions.values()
.stream()
.collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getUuid, RuleDefinitionDto::getKey));
this.params = new HashMap<>(ruleUuidsByKey.size());
dbClient.ruleDao().selectRuleParamsByRuleKeys(session, definitions.keySet())
.forEach(ruleParam -> params.compute(
ruleUuidsByKey.get(ruleParam.getRuleUuid()),
(key, value) -> {
if (value == null) {
return ImmutableSet.of(ruleParam);
}
return ImmutableSet.copyOf(Sets.union(value, Collections.singleton(ruleParam)));
}));
if (ruleKey.isPresent()) {
params.computeIfAbsent(ruleKey.get(), r -> new HashSet<>()).add(ruleParam);
}
}
}

private Optional<RuleDefinitionDto> getDefinition(RuleKey ruleKey) {
return Optional.ofNullable(definitions.get(requireNonNull(ruleKey, "RuleKey can't be null")));
return ruleFinder.findDtoByKey(requireNonNull(ruleKey, "RuleKey can't be null"));
}

private Set<RuleParamDto> getRuleParams(RuleKey ruleKey) {
Set<RuleParamDto> res = params.get(requireNonNull(ruleKey, "RuleKey can't be null"));
return res == null ? Collections.emptySet() : res;
return params.getOrDefault(requireNonNull(ruleKey, "RuleKey can't be null"), emptySet());
}
}
}

+ 7
- 4
server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImpl.java View File

@@ -46,6 +46,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.rule.ServerRuleFinder;

import static com.google.common.base.Preconditions.checkState;

@@ -54,6 +55,7 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository
private static final String DEFAULT_PROFILE_NAME = "Sonar way";

private final DbClient dbClient;
private final ServerRuleFinder ruleFinder;
private final Languages languages;
private final List<BuiltInQualityProfilesDefinition> definitions;
private List<BuiltInQProfile> qProfiles;
@@ -61,12 +63,13 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository
/**
* Requires for pico container when no {@link BuiltInQualityProfilesDefinition} is defined at all
*/
public BuiltInQProfileRepositoryImpl(DbClient dbClient, Languages languages) {
this(dbClient, languages, new BuiltInQualityProfilesDefinition[0]);
public BuiltInQProfileRepositoryImpl(DbClient dbClient, ServerRuleFinder ruleFinder, Languages languages) {
this(dbClient, ruleFinder, languages, new BuiltInQualityProfilesDefinition[0]);
}

public BuiltInQProfileRepositoryImpl(DbClient dbClient, Languages languages, BuiltInQualityProfilesDefinition... definitions) {
public BuiltInQProfileRepositoryImpl(DbClient dbClient, ServerRuleFinder ruleFinder, Languages languages, BuiltInQualityProfilesDefinition... definitions) {
this.dbClient = dbClient;
this.ruleFinder = ruleFinder;
this.languages = languages;
this.definitions = ImmutableList.copyOf(definitions);
}
@@ -141,7 +144,7 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository

private Map<RuleKey, RuleDefinitionDto> loadRuleDefinitionsByRuleKey() {
try (DbSession dbSession = dbClient.openSession(false)) {
List<RuleDefinitionDto> ruleDefinitions = dbClient.ruleDao().selectAllDefinitions(dbSession);
Collection<RuleDefinitionDto> ruleDefinitions = ruleFinder.findAll();
Multimap<String, DeprecatedRuleKeyDto> deprecatedRuleKeysByRuleId = dbClient.ruleDao().selectAllDeprecatedRuleKeys(dbSession).stream()
.collect(MoreCollectors.index(DeprecatedRuleKeyDto::getRuleUuid));
Map<RuleKey, RuleDefinitionDto> rulesByRuleKey = new HashMap<>();

+ 5
- 10
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/DefaultAdminCredentialsVerifierImplTest.java View File

@@ -52,11 +52,6 @@ public class DefaultAdminCredentialsVerifierImplTest {

private final DefaultAdminCredentialsVerifierImpl underTest = new DefaultAdminCredentialsVerifierImpl(db.getDbClient(), localAuthentication, notificationManager);

@After
public void after() {
underTest.stop();
}

@Test
public void correctly_detect_if_admin_account_is_used_with_default_credential() {
UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN));
@@ -77,7 +72,7 @@ public class DefaultAdminCredentialsVerifierImplTest {
UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN));
changePassword(admin, "admin");

underTest.start();
underTest.runAtStart();

assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isTrue();
assertThat(logTester.logs(LoggerLevel.WARN)).contains("Default Administrator credentials are still being used. Make sure to change the password or deactivate the account.");
@@ -92,7 +87,7 @@ public class DefaultAdminCredentialsVerifierImplTest {
db.getDbClient().internalPropertiesDao().save(db.getSession(), DEFAULT_ADMIN_CREDENTIAL_USAGE_EMAIL, "true");
db.commit();

underTest.start();
underTest.runAtStart();

verifyNoMoreInteractions(notificationManager);
}
@@ -102,7 +97,7 @@ public class DefaultAdminCredentialsVerifierImplTest {
UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN));
changePassword(admin, "something_else");

underTest.start();
underTest.runAtStart();

assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isFalse();
assertThat(logTester.logs()).isEmpty();
@@ -114,7 +109,7 @@ public class DefaultAdminCredentialsVerifierImplTest {
UserDto otherUser = db.users().insertUser();
changePassword(otherUser, "admin");

underTest.start();
underTest.runAtStart();

assertThat(db.users().selectUserByLogin(otherUser.getLogin()).get().isResetPassword()).isFalse();
assertThat(logTester.logs()).isEmpty();
@@ -126,7 +121,7 @@ public class DefaultAdminCredentialsVerifierImplTest {
UserDto admin = db.users().insertUser(u -> u.setLogin(ADMIN_LOGIN).setActive(false));
changePassword(admin, "admin");

underTest.start();
underTest.runAtStart();

assertThat(db.users().selectUserByLogin(admin.getLogin()).get().isResetPassword()).isFalse();
assertThat(logTester.logs()).isEmpty();

+ 66
- 11
server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java View File

@@ -19,11 +19,13 @@
*/
package org.sonar.server.qualityprofile;

import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.PropertyType;
import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
import org.sonar.api.rule.Severity;
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
@@ -40,10 +42,14 @@ import org.sonar.db.qualityprofile.QProfileChangeDto;
import org.sonar.db.qualityprofile.QProfileChangeQuery;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleParamDto;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.DefaultRuleFinder;
import org.sonar.server.rule.ServerRuleFinder;
import org.sonar.server.util.StringTypeValidation;
import org.sonar.server.util.TypeValidations;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

@@ -56,13 +62,14 @@ public class BuiltInQProfileInsertImplTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();

private System2 system2 = new AlwaysIncreasingSystem2();
private UuidFactory uuidFactory = new SequenceUuidFactory();
private TypeValidations typeValidations = new TypeValidations(emptyList());
private DbSession dbSession = db.getSession();
private DbSession batchDbSession = db.getDbClient().openSession(true);
private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
private BuiltInQProfileInsertImpl underTest = new BuiltInQProfileInsertImpl(db.getDbClient(), system2, uuidFactory, typeValidations, activeRuleIndexer);
private final System2 system2 = new AlwaysIncreasingSystem2();
private final UuidFactory uuidFactory = new SequenceUuidFactory();
private final TypeValidations typeValidations = new TypeValidations(singletonList(new StringTypeValidation()));
private final DbSession dbSession = db.getSession();
private final DbSession batchDbSession = db.getDbClient().openSession(true);
private final ServerRuleFinder ruleFinder = new DefaultRuleFinder(db.getDbClient());
private final ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
private final BuiltInQProfileInsertImpl underTest = new BuiltInQProfileInsertImpl(db.getDbClient(), ruleFinder, system2, uuidFactory, typeValidations, activeRuleIndexer);

@After
public void tearDown() {
@@ -85,15 +92,63 @@ public class BuiltInQProfileInsertImplTest {
call(builtIn);

verifyTableSize("rules_profiles", 1);
verifyTableSize("org_qprofiles", 1);
verifyTableSize("active_rules", 2);
verifyTableSize("active_rule_parameters", 0);
verifyTableSize("qprofile_changes", 2);
verifyTableSize("default_qprofiles", 0);


QProfileDto profile = verifyProfileInDb(builtIn);
verifyActiveRuleInDb(profile, rule1, Severity.CRITICAL);
verifyActiveRuleInDb(profile, rule2, Severity.MAJOR);
}

@Test
public void insert_default_qp() {
BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
context.createBuiltInQualityProfile("the name", "xoo")
.setDefault(true)
.done();

BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name"));
call(builtIn);

verifyTableSize("rules_profiles", 1);
verifyTableSize("org_qprofiles", 1);
verifyTableSize("active_rules", 0);
verifyTableSize("active_rule_parameters", 0);
verifyTableSize("qprofile_changes", 0);
verifyTableSize("default_qprofiles", 1);

verifyProfileInDb(builtIn);
}

@Test
public void insert_active_rules_with_params() {
RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
RuleParamDto param1 = db.rules().insertRuleParam(rule1, p -> p.setType(PropertyType.STRING.name()));
RuleParamDto param2 = db.rules().insertRuleParam(rule1, p -> p.setType(PropertyType.STRING.name()));

BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("the name", "xoo");

newQp.activateRule(rule1.getRepositoryKey(), rule1.getRuleKey()).overrideSeverity(Severity.CRITICAL);
newQp.done();

BuiltInQProfile builtIn = builtInQProfileRepository.create(context.profile("xoo", "the name"), rule1);
call(builtIn);

verifyTableSize("rules_profiles", 1);
verifyTableSize("org_qprofiles", 1);
verifyTableSize("active_rules", 1);
verifyTableSize("active_rule_parameters", 2);
verifyTableSize("qprofile_changes", 1);

QProfileDto profile = verifyProfileInDb(builtIn);
verifyActiveRuleInDb(profile, rule1, Severity.CRITICAL, param1, param2);
}

@Test
public void flag_profile_as_default_if_declared_as_default_by_api() {
BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
@@ -123,6 +178,7 @@ public class BuiltInQProfileInsertImplTest {

QProfileDto defaultProfile = db.getDbClient().qualityProfileDao().selectDefaultProfile(dbSession, "xoo");
assertThat(defaultProfile.getKee()).isEqualTo(currentDefault.getKee());
verifyTableSize("rules_profiles", 2);
}

@Test
@@ -138,10 +194,9 @@ public class BuiltInQProfileInsertImplTest {
assertThat(defaultProfile).isNull();
}

// TODO test params
// TODO test lot of active_rules, params, orgas

private void verifyActiveRuleInDb(QProfileDto profile, RuleDefinitionDto rule, String expectedSeverity) {
private void verifyActiveRuleInDb(QProfileDto profile, RuleDefinitionDto rule, String expectedSeverity, RuleParamDto... paramDtos) {
ActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByKey(dbSession, ActiveRuleKey.of(profile, rule.getKey())).get();
assertThat(activeRule.getUuid()).isNotNull();
assertThat(activeRule.getInheritance()).isNull();
@@ -153,7 +208,7 @@ public class BuiltInQProfileInsertImplTest {
assertThat(activeRule.getUpdatedAt()).isPositive();

List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(dbSession, activeRule.getUuid());
assertThat(params).isEmpty();
assertThat(params).extracting(ActiveRuleParamDto::getKey).containsOnly(Arrays.stream(paramDtos).map(RuleParamDto::getName).toArray(String[]::new));

QProfileChangeQuery changeQuery = new QProfileChangeQuery(profile.getKee());
QProfileChangeDto change = db.getDbClient().qProfileChangeDao().selectByQuery(dbSession, changeQuery).stream()

+ 28
- 26
server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileRepositoryImplTest.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.qualityprofile;

import java.util.ArrayList;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
@@ -32,6 +31,8 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.language.LanguageTesting;
import org.sonar.server.rule.DefaultRuleFinder;
import org.sonar.server.rule.ServerRuleFinder;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@@ -51,16 +52,16 @@ public class BuiltInQProfileRepositoryImplTest {
@Rule
public DbTester db = DbTester.create();

private DbClient dbClient = db.getDbClient();
private final DbClient dbClient = db.getDbClient();
private final ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient);

@Test
public void create_qprofile_with_rule() {
RuleDefinitionDto rule1 = db.rules().insert();
RuleDefinitionDto rule2 = db.rules().insert();
db.rules().insert();
List<DummyProfileDefinition> definitions = singletonList(new DummyProfileDefinition("foo", "foo", false,
asList(rule1.getKey(), rule2.getKey())));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));
DummyProfileDefinition definition = new DummyProfileDefinition("foo", "foo", false, asList(rule1.getKey(), rule2.getKey()));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definition);

underTest.initialize();

@@ -76,7 +77,8 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void make_single_profile_of_a_language_default_even_if_not_flagged_as_so() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), new DummyProfileDefinition("foo", "foo1", false));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "foo1", false));

underTest.initialize();

@@ -87,7 +89,8 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void make_single_profile_of_a_language_default_even_if_flagged_as_so() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), new DummyProfileDefinition("foo", "foo1", true));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "foo1", true));

underTest.initialize();

@@ -98,11 +101,10 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void make_first_profile_of_a_language_default_when_none_flagged_as_so() {
List<DummyProfileDefinition> definitions = new ArrayList<>(
asList(new DummyProfileDefinition("foo", "foo1", false), new DummyProfileDefinition("foo", "foo2", false)));
String firstName = definitions.get(0).getName();
String secondName = definitions.get(1).getName();
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));
DummyProfileDefinition[] definitions = new DummyProfileDefinition[] {new DummyProfileDefinition("foo", "foo1", false), new DummyProfileDefinition("foo", "foo2", false)};
String firstName = definitions[0].getName();
String secondName = definitions[1].getName();
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definitions);

underTest.initialize();

@@ -114,7 +116,7 @@ public class BuiltInQProfileRepositoryImplTest {
@Test
public void create_profile_Sonar_Way_as_default_if_none_other_is_defined_default_for_a_given_language() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(
dbClient, new Languages(FOO_LANGUAGE),
dbClient, ruleFinder, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "doh", false), new DummyProfileDefinition("foo", "boo", false),
new DummyProfileDefinition("foo", SONAR_WAY_QP_NAME, false), new DummyProfileDefinition("foo", "goo", false));

@@ -130,7 +132,7 @@ public class BuiltInQProfileRepositoryImplTest {
@Test
public void do_not_create_Sonar_Way_as_default_if_other_profile_is_defined_as_default() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(
dbClient, new Languages(FOO_LANGUAGE),
dbClient, ruleFinder, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", SONAR_WAY_QP_NAME, false), new DummyProfileDefinition("foo", "goo", true));

underTest.initialize();
@@ -146,7 +148,7 @@ public class BuiltInQProfileRepositoryImplTest {
public void match_Sonar_Way_default_with_case_sensitivity() {
String sonarWayInOtherCase = SONAR_WAY_QP_NAME.toUpperCase();
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(
dbClient, new Languages(FOO_LANGUAGE),
dbClient, ruleFinder, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "goo", false), new DummyProfileDefinition("foo", sonarWayInOtherCase, false));

underTest.initialize();
@@ -160,7 +162,8 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void create_no_BuiltInQProfile_when_all_definitions_apply_to_non_defined_languages() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(), new DummyProfileDefinition("foo", "P1", false));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages(),
new DummyProfileDefinition("foo", "P1", false));

underTest.initialize();

@@ -172,9 +175,9 @@ public class BuiltInQProfileRepositoryImplTest {
RuleDefinitionDto rule1 = db.rules().insert();
db.rules().insertDeprecatedKey(d -> d.setRuleUuid(rule1.getUuid()).setOldRepositoryKey("oldRepo").setOldRuleKey("oldKey"));
RuleDefinitionDto rule2 = db.rules().insert();
List<DummyProfileDefinition> definitions = singletonList(new DummyProfileDefinition("foo", "foo", false,
asList(RuleKey.of("oldRepo", "oldKey"), rule2.getKey())));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));
DummyProfileDefinition definition = new DummyProfileDefinition("foo", "foo", false,
asList(RuleKey.of("oldRepo", "oldKey"), rule2.getKey()));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definition);

underTest.initialize();

@@ -190,8 +193,8 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void fail_with_ISE_when_rule_does_not_exist() {
List<DummyProfileDefinition> definitions = singletonList(new DummyProfileDefinition("foo", "foo", false, singletonList(EXTERNAL_XOO)));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE), definitions.toArray(new BuiltInQualityProfilesDefinition[0]));
DummyProfileDefinition[] definitions = new DummyProfileDefinition[] {new DummyProfileDefinition("foo", "foo", false, singletonList(EXTERNAL_XOO))};
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE), definitions);

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(String.format("Rule with key '%s' not found", EXTERNAL_XOO.toString()));
@@ -201,9 +204,8 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void fail_with_ISE_when_two_profiles_with_different_name_are_default_for_the_same_language() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, new Languages(FOO_LANGUAGE),
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(dbClient, ruleFinder, new Languages(FOO_LANGUAGE),
new DummyProfileDefinition("foo", "foo1", true), new DummyProfileDefinition("foo", "foo2", true));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Several Quality profiles are flagged as default for the language foo: [foo1, foo2]");

@@ -212,7 +214,7 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void get_throws_ISE_if_called_before_initialize() {
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages());
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages());

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("initialize must be called first");
@@ -222,7 +224,7 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void initialize_throws_ISE_if_called_twice() {
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages());
BuiltInQProfileRepositoryImpl underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages());
underTest.initialize();

expectedException.expect(IllegalStateException.class);
@@ -233,7 +235,7 @@ public class BuiltInQProfileRepositoryImplTest {

@Test
public void initialize_throws_ISE_if_language_has_no_builtin_qp() {
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(FOO_LANGUAGE));
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), ruleFinder, new Languages(FOO_LANGUAGE));

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("The following languages have no built-in quality profiles: foo");

+ 99
- 81
server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java View File

@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -35,7 +36,6 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.picocontainer.Startable;
import org.sonar.api.resources.Languages;
@@ -72,7 +72,9 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableMap;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
@@ -113,7 +115,7 @@ public class RegisterRules implements Startable {
@Override
public void start() {
Profiler profiler = Profiler.create(LOG).startInfo("Register rules");
try (DbSession dbSession = dbClient.openSession(false)) {
try (DbSession dbSession = dbClient.openSession(true)) {
RulesDefinition.Context ruleDefinitionContext = defLoader.load();
List<RulesDefinition.Repository> repositories = ruleDefinitionContext.repositories();
RegisterRulesContext registerRulesContext = createRegisterRulesContext(dbSession);
@@ -122,9 +124,7 @@ public class RegisterRules implements Startable {

for (RulesDefinition.ExtendedRepository repoDef : repositories) {
if (languages.get(repoDef.language()) != null) {
for (RulesDefinition.Rule ruleDef : repoDef.rules()) {
registerRule(registerRulesContext, ruleDef, dbSession);
}
registerRules(registerRulesContext, repoDef.rules(), dbSession);
dbSession.commit();
}
}
@@ -153,7 +153,13 @@ public class RegisterRules implements Startable {
Map<RuleKey, RuleDefinitionDto> allRules = dbClient.ruleDao().selectAllDefinitions(dbSession).stream()
.collect(uniqueIndex(RuleDefinitionDto::getKey));
Map<String, Set<SingleDeprecatedRuleKey>> existingDeprecatedKeysById = loadDeprecatedRuleKeys(dbSession);
return new RegisterRulesContext(allRules, existingDeprecatedKeysById);
Map<String, List<RuleParamDto>> ruleParamsByRuleUuid = loadAllRuleParameters(dbSession);
return new RegisterRulesContext(allRules, existingDeprecatedKeysById, ruleParamsByRuleUuid);
}

private Map<String, List<RuleParamDto>> loadAllRuleParameters(DbSession dbSession) {
return dbClient.ruleDao().selectAllRuleParams(dbSession).stream()
.collect(Collectors.groupingBy(RuleParamDto::getRuleUuid));
}

private Map<String, Set<SingleDeprecatedRuleKey>> loadDeprecatedRuleKeys(DbSession dbSession) {
@@ -167,6 +173,7 @@ public class RegisterRules implements Startable {
private final Map<RuleKey, RuleDefinitionDto> dbRules;
private final Set<RuleDefinitionDto> known;
private final Map<String, Set<SingleDeprecatedRuleKey>> dbDeprecatedKeysByUuid;
private final Map<String, List<RuleParamDto>> ruleParamsByRuleUuid;
private final Map<RuleKey, RuleDefinitionDto> dbRulesByDbDeprecatedKey;
// mutable data
private final Set<RuleDefinitionDto> created = new HashSet<>();
@@ -175,10 +182,12 @@ public class RegisterRules implements Startable {
private final Set<RuleDefinitionDto> unchanged = new HashSet<>();
private final Set<RuleDefinitionDto> removed = new HashSet<>();

private RegisterRulesContext(Map<RuleKey, RuleDefinitionDto> dbRules, Map<String, Set<SingleDeprecatedRuleKey>> dbDeprecatedKeysByUuid) {
private RegisterRulesContext(Map<RuleKey, RuleDefinitionDto> dbRules, Map<String, Set<SingleDeprecatedRuleKey>> dbDeprecatedKeysByUuid,
Map<String, List<RuleParamDto>> ruleParamsByRuleUuid) {
this.dbRules = ImmutableMap.copyOf(dbRules);
this.known = ImmutableSet.copyOf(dbRules.values());
this.dbDeprecatedKeysByUuid = dbDeprecatedKeysByUuid;
this.ruleParamsByRuleUuid = ruleParamsByRuleUuid;
this.dbRulesByDbDeprecatedKey = buildDbRulesByDbDeprecatedKey(dbDeprecatedKeysByUuid, dbRules);
}

@@ -187,19 +196,19 @@ public class RegisterRules implements Startable {
Map<String, RuleDefinitionDto> dbRulesByRuleUuid = dbRules.values().stream()
.collect(uniqueIndex(RuleDefinitionDto::getUuid));

ImmutableMap.Builder<RuleKey, RuleDefinitionDto> builder = ImmutableMap.builder();
Map<RuleKey, RuleDefinitionDto> rulesByKey = new LinkedHashMap<>();
for (Map.Entry<String, Set<SingleDeprecatedRuleKey>> entry : dbDeprecatedKeysByUuid.entrySet()) {
String ruleUuid = entry.getKey();
RuleDefinitionDto rule = dbRulesByRuleUuid.get(ruleUuid);
if (rule == null) {
LOG.warn("Could not retrieve rule with uuid %s referenced by a deprecated rule key. " +
"The following deprecated rule keys seem to be referencing a non-existing rule",
"The following deprecated rule keys seem to be referencing a non-existing rule",
ruleUuid, entry.getValue());
} else {
entry.getValue().forEach(d -> builder.put(d.getOldRuleKeyAsRuleKey(), rule));
entry.getValue().forEach(d -> rulesByKey.put(d.getOldRuleKeyAsRuleKey(), rule));
}
}
return builder.build();
return unmodifiableMap(rulesByKey);
}

private boolean hasDbRules() {
@@ -219,7 +228,7 @@ public class RegisterRules implements Startable {
return res;
}

private ImmutableMap<RuleKey, SingleDeprecatedRuleKey> getDbDeprecatedKeysByOldRuleKey() {
private Map<RuleKey, SingleDeprecatedRuleKey> getDbDeprecatedKeysByOldRuleKey() {
return dbDeprecatedKeysByUuid.values().stream()
.flatMap(Collection::stream)
.collect(uniqueIndex(SingleDeprecatedRuleKey::getOldRuleKeyAsRuleKey));
@@ -229,6 +238,10 @@ public class RegisterRules implements Startable {
return dbDeprecatedKeysByUuid.getOrDefault(rule.getUuid(), emptySet());
}

private List<RuleParamDto> getRuleParametersFor(String ruleUuid) {
return ruleParamsByRuleUuid.getOrDefault(ruleUuid, emptyList());
}

private Stream<RuleDefinitionDto> getRemaining() {
Set<RuleDefinitionDto> res = new HashSet<>(dbRules.values());
res.removeAll(unchanged);
@@ -298,8 +311,7 @@ public class RegisterRules implements Startable {
}

private void persistRepositories(DbSession dbSession, List<RulesDefinition.Repository> repositories) {
List<RuleRepositoryDto> dtos = repositories
.stream()
List<RuleRepositoryDto> dtos = repositories.stream()
.map(r -> new RuleRepositoryDto(r.key(), r.language(), r.name()))
.collect(toList(repositories.size()));
List<String> keys = dtos.stream().map(RuleRepositoryDto::getKey).collect(toList(repositories.size()));
@@ -313,46 +325,53 @@ public class RegisterRules implements Startable {
// nothing
}

private void registerRule(RegisterRulesContext context, RulesDefinition.Rule ruleDef, DbSession session) {
RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key());
private void registerRules(RegisterRulesContext context, List<RulesDefinition.Rule> ruleDefs, DbSession session) {
Map<RulesDefinition.Rule, RuleDefinitionDto> dtos = new LinkedHashMap<>(ruleDefs.size());

RuleDefinitionDto ruleDefinitionDto = context.getDbRuleFor(ruleDef)
.orElseGet(() -> {
RuleDefinitionDto newRule = createRuleDto(ruleDef, session);
context.created(newRule);
return newRule;
});
for (RulesDefinition.Rule ruleDef : ruleDefs) {
RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key());

// we must detect renaming __before__ we modify the DTO
if (!ruleDefinitionDto.getKey().equals(ruleKey)) {
context.renamed(ruleDefinitionDto);
ruleDefinitionDto.setRuleKey(ruleKey);
}
RuleDefinitionDto ruleDefinitionDto = context.getDbRuleFor(ruleDef)
.orElseGet(() -> {
RuleDefinitionDto newRule = createRuleDto(ruleDef, session);
context.created(newRule);
return newRule;
});
dtos.put(ruleDef, ruleDefinitionDto);

// we must detect renaming __before__ we modify the DTO
if (!ruleDefinitionDto.getKey().equals(ruleKey)) {
context.renamed(ruleDefinitionDto);
ruleDefinitionDto.setRuleKey(ruleKey);
}

if (mergeRule(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}
if (mergeRule(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}

if (mergeDebtDefinitions(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}
if (mergeDebtDefinitions(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}

if (mergeTags(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}
if (mergeTags(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}

if (mergeSecurityStandards(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}
if (mergeSecurityStandards(ruleDef, ruleDefinitionDto)) {
context.updated(ruleDefinitionDto);
}

if (context.isUpdated(ruleDefinitionDto) || context.isRenamed(ruleDefinitionDto)) {
update(session, ruleDefinitionDto);
} else if (!context.isCreated(ruleDefinitionDto)) {
context.unchanged(ruleDefinitionDto);
if (context.isUpdated(ruleDefinitionDto) || context.isRenamed(ruleDefinitionDto)) {
update(session, ruleDefinitionDto);
} else if (!context.isCreated(ruleDefinitionDto)) {
context.unchanged(ruleDefinitionDto);
}
}

mergeParams(ruleDef, ruleDefinitionDto, session);
updateDeprecatedKeys(context, ruleDef, ruleDefinitionDto, session);
for (Map.Entry<RulesDefinition.Rule, RuleDefinitionDto> e : dtos.entrySet()) {
mergeParams(context, e.getKey(), e.getValue(), session);
updateDeprecatedKeys(context, e.getKey(), e.getValue(), session);
}
}

private RuleDefinitionDto createRuleDto(RulesDefinition.Rule ruleDef, DbSession session) {
@@ -409,23 +428,23 @@ public class RegisterRules implements Startable {

private static boolean mergeRule(RulesDefinition.Rule def, RuleDefinitionDto dto) {
boolean changed = false;
if (!StringUtils.equals(dto.getName(), def.name())) {
if (!Objects.equals(dto.getName(), def.name())) {
dto.setName(def.name());
changed = true;
}
if (mergeDescription(def, dto)) {
changed = true;
}
if (!StringUtils.equals(dto.getPluginKey(), def.pluginKey())) {
if (!Objects.equals(dto.getPluginKey(), def.pluginKey())) {
dto.setPluginKey(def.pluginKey());
changed = true;
}
if (!StringUtils.equals(dto.getConfigKey(), def.internalKey())) {
if (!Objects.equals(dto.getConfigKey(), def.internalKey())) {
dto.setConfigKey(def.internalKey());
changed = true;
}
String severity = def.severity();
if (!ObjectUtils.equals(dto.getSeverityString(), severity)) {
if (!Objects.equals(dto.getSeverityString(), severity)) {
dto.setSeverity(severity);
changed = true;
}
@@ -438,16 +457,16 @@ public class RegisterRules implements Startable {
dto.setStatus(def.status());
changed = true;
}
if (!StringUtils.equals(dto.getScope().name(), def.scope().name())) {
if (!Objects.equals(dto.getScope().name(), def.scope().name())) {
dto.setScope(toDtoScope(def.scope()));
changed = true;
}
if (!StringUtils.equals(dto.getLanguage(), def.repository().language())) {
if (!Objects.equals(dto.getLanguage(), def.repository().language())) {
dto.setLanguage(def.repository().language());
changed = true;
}
RuleType type = RuleType.valueOf(def.type().name());
if (!ObjectUtils.equals(dto.getType(), type.getDbConstant())) {
if (!Objects.equals(dto.getType(), type.getDbConstant())) {
dto.setType(type);
changed = true;
}
@@ -460,11 +479,11 @@ public class RegisterRules implements Startable {

private static boolean mergeDescription(RulesDefinition.Rule def, RuleDefinitionDto dto) {
boolean changed = false;
if (def.htmlDescription() != null && !StringUtils.equals(dto.getDescription(), def.htmlDescription())) {
if (def.htmlDescription() != null && !Objects.equals(dto.getDescription(), def.htmlDescription())) {
dto.setDescription(def.htmlDescription());
dto.setDescriptionFormat(Format.HTML);
changed = true;
} else if (def.markdownDescription() != null && !StringUtils.equals(dto.getDescription(), def.markdownDescription())) {
} else if (def.markdownDescription() != null && !Objects.equals(dto.getDescription(), def.markdownDescription())) {
dto.setDescription(def.markdownDescription());
dto.setDescriptionFormat(Format.MARKDOWN);
changed = true;
@@ -490,27 +509,27 @@ public class RegisterRules implements Startable {
@Nullable String remediationCoefficient, @Nullable String remediationOffset, @Nullable String effortToFixDescription) {
boolean changed = false;

if (!StringUtils.equals(dto.getDefRemediationFunction(), remediationFunction)) {
if (!Objects.equals(dto.getDefRemediationFunction(), remediationFunction)) {
dto.setDefRemediationFunction(remediationFunction);
changed = true;
}
if (!StringUtils.equals(dto.getDefRemediationGapMultiplier(), remediationCoefficient)) {
if (!Objects.equals(dto.getDefRemediationGapMultiplier(), remediationCoefficient)) {
dto.setDefRemediationGapMultiplier(remediationCoefficient);
changed = true;
}
if (!StringUtils.equals(dto.getDefRemediationBaseEffort(), remediationOffset)) {
if (!Objects.equals(dto.getDefRemediationBaseEffort(), remediationOffset)) {
dto.setDefRemediationBaseEffort(remediationOffset);
changed = true;
}
if (!StringUtils.equals(dto.getGapDescription(), effortToFixDescription)) {
if (!Objects.equals(dto.getGapDescription(), effortToFixDescription)) {
dto.setGapDescription(effortToFixDescription);
changed = true;
}
return changed;
}

private void mergeParams(RulesDefinition.Rule ruleDef, RuleDefinitionDto rule, DbSession session) {
List<RuleParamDto> paramDtos = dbClient.ruleDao().selectRuleParamsByRuleKey(session, rule.getKey());
private void mergeParams(RegisterRulesContext context, RulesDefinition.Rule ruleDef, RuleDefinitionDto rule, DbSession session) {
List<RuleParamDto> paramDtos = context.getRuleParametersFor(rule.getUuid());
Map<String, RuleParamDto> existingParamsByName = new HashMap<>();

Profiler profiler = Profiler.create(Loggers.get(getClass()));
@@ -556,23 +575,22 @@ public class RegisterRules implements Startable {

private static boolean mergeParam(RuleParamDto paramDto, RulesDefinition.Param paramDef) {
boolean changed = false;
if (!StringUtils.equals(paramDto.getType(), paramDef.type().toString())) {
if (!Objects.equals(paramDto.getType(), paramDef.type().toString())) {
paramDto.setType(paramDef.type().toString());
changed = true;
}
if (!StringUtils.equals(paramDto.getDefaultValue(), paramDef.defaultValue())) {
if (!Objects.equals(paramDto.getDefaultValue(), paramDef.defaultValue())) {
paramDto.setDefaultValue(paramDef.defaultValue());
changed = true;
}
if (!StringUtils.equals(paramDto.getDescription(), paramDef.description())) {
if (!Objects.equals(paramDto.getDescription(), paramDef.description())) {
paramDto.setDescription(paramDef.description());
changed = true;
}
return changed;
}

private void updateDeprecatedKeys(RegisterRulesContext context, RulesDefinition.Rule ruleDef, RuleDefinitionDto rule,
DbSession dbSession) {
private void updateDeprecatedKeys(RegisterRulesContext context, RulesDefinition.Rule ruleDef, RuleDefinitionDto rule, DbSession dbSession) {

Set<SingleDeprecatedRuleKey> deprecatedRuleKeysFromDefinition = SingleDeprecatedRuleKey.from(ruleDef);
Set<SingleDeprecatedRuleKey> deprecatedRuleKeysFromDB = context.getDBDeprecatedKeysFor(rule);
@@ -604,9 +622,9 @@ public class RegisterRules implements Startable {
changed = true;
} else if (dto.getSystemTags().size() != ruleDef.tags().size() ||
!dto.getSystemTags().containsAll(ruleDef.tags())) {
dto.setSystemTags(ruleDef.tags());
changed = true;
}
dto.setSystemTags(ruleDef.tags());
changed = true;
}
return changed;
}

@@ -618,9 +636,9 @@ public class RegisterRules implements Startable {
changed = true;
} else if (dto.getSecurityStandards().size() != ruleDef.securityStandards().size() ||
!dto.getSecurityStandards().containsAll(ruleDef.securityStandards())) {
dto.setSecurityStandards(ruleDef.securityStandards());
changed = true;
}
dto.setSecurityStandards(ruleDef.securityStandards());
changed = true;
}
return changed;
}

@@ -669,31 +687,31 @@ public class RegisterRules implements Startable {

private static boolean updateCustomRuleFromTemplateRule(RuleDefinitionDto customRule, RuleDefinitionDto templateRule) {
boolean changed = false;
if (!StringUtils.equals(customRule.getLanguage(), templateRule.getLanguage())) {
if (!Objects.equals(customRule.getLanguage(), templateRule.getLanguage())) {
customRule.setLanguage(templateRule.getLanguage());
changed = true;
}
if (!StringUtils.equals(customRule.getConfigKey(), templateRule.getConfigKey())) {
if (!Objects.equals(customRule.getConfigKey(), templateRule.getConfigKey())) {
customRule.setConfigKey(templateRule.getConfigKey());
changed = true;
}
if (!StringUtils.equals(customRule.getPluginKey(), templateRule.getPluginKey())) {
if (!Objects.equals(customRule.getPluginKey(), templateRule.getPluginKey())) {
customRule.setPluginKey(templateRule.getPluginKey());
changed = true;
}
if (!StringUtils.equals(customRule.getDefRemediationFunction(), templateRule.getDefRemediationFunction())) {
if (!Objects.equals(customRule.getDefRemediationFunction(), templateRule.getDefRemediationFunction())) {
customRule.setDefRemediationFunction(templateRule.getDefRemediationFunction());
changed = true;
}
if (!StringUtils.equals(customRule.getDefRemediationGapMultiplier(), templateRule.getDefRemediationGapMultiplier())) {
if (!Objects.equals(customRule.getDefRemediationGapMultiplier(), templateRule.getDefRemediationGapMultiplier())) {
customRule.setDefRemediationGapMultiplier(templateRule.getDefRemediationGapMultiplier());
changed = true;
}
if (!StringUtils.equals(customRule.getDefRemediationBaseEffort(), templateRule.getDefRemediationBaseEffort())) {
if (!Objects.equals(customRule.getDefRemediationBaseEffort(), templateRule.getDefRemediationBaseEffort())) {
customRule.setDefRemediationBaseEffort(templateRule.getDefRemediationBaseEffort());
changed = true;
}
if (!StringUtils.equals(customRule.getGapDescription(), templateRule.getGapDescription())) {
if (!Objects.equals(customRule.getGapDescription(), templateRule.getGapDescription())) {
customRule.setGapDescription(templateRule.getGapDescription());
changed = true;
}
@@ -701,11 +719,11 @@ public class RegisterRules implements Startable {
customRule.setStatus(templateRule.getStatus());
changed = true;
}
if (!StringUtils.equals(customRule.getSeverityString(), templateRule.getSeverityString())) {
if (!Objects.equals(customRule.getSeverityString(), templateRule.getSeverityString())) {
customRule.setSeverity(templateRule.getSeverityString());
changed = true;
}
if (!StringUtils.equals(customRule.getRepositoryKey(), templateRule.getRepositoryKey())) {
if (!Objects.equals(customRule.getRepositoryKey(), templateRule.getRepositoryKey())) {
customRule.setRepositoryKey(templateRule.getRepositoryKey());
changed = true;
}
@@ -771,7 +789,7 @@ public class RegisterRules implements Startable {
lazyToString(() -> intersection.stream().map(RuleKey::toString).collect(Collectors.joining(","))));

// Find incorrect usage of deprecated keys
ImmutableMap<RuleKey, SingleDeprecatedRuleKey> dbDeprecatedRuleKeysByOldRuleKey = registerRulesContext.getDbDeprecatedKeysByOldRuleKey();
Map<RuleKey, SingleDeprecatedRuleKey> dbDeprecatedRuleKeysByOldRuleKey = registerRulesContext.getDbDeprecatedKeysByOldRuleKey();

Set<String> incorrectRuleKeyMessage = definedRules.stream()
.flatMap(r -> filterInvalidDeprecatedRuleKeys(dbDeprecatedRuleKeysByOldRuleKey, r))

+ 4
- 4
server/sonar-webserver-core/src/main/java/org/sonar/server/startup/RegisterMetrics.java View File

@@ -19,9 +19,6 @@
*/
package org.sonar.server.startup;

import static com.google.common.collect.FluentIterable.concat;
import static com.google.common.collect.Lists.newArrayList;

import com.google.common.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.List;
@@ -39,6 +36,9 @@ import org.sonar.db.DbSession;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.metric.MetricToDto;

import static com.google.common.collect.FluentIterable.concat;
import static com.google.common.collect.Lists.newArrayList;

public class RegisterMetrics implements Startable {

private static final Logger LOG = Loggers.get(RegisterMetrics.class);
@@ -72,7 +72,7 @@ public class RegisterMetrics implements Startable {

void register(Iterable<Metric> metrics) {
Profiler profiler = Profiler.create(LOG).startInfo("Register metrics");
try (DbSession session = dbClient.openSession(false)) {
try (DbSession session = dbClient.openSession(true)) {
save(session, metrics);
sanitizeQualityGates(session);
session.commit();

+ 2
- 1
server/sonar-webserver-core/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java View File

@@ -95,6 +95,7 @@ public class CachingRuleFinderTest {

verify(dbClient).openSession(anyBoolean());
verify(ruleDao).selectAllDefinitions(dbSession);
verify(ruleDao).selectAllRuleParams(dbSession);
verifyNoMoreInteractions(ruleDao);
}

@@ -110,7 +111,7 @@ public class CachingRuleFinderTest {

new CachingRuleFinder(dbClient);

verify(ruleDao).selectRuleParamsByRuleKeys(dbSession, ImmutableSet.copyOf(ruleKeys));
verify(ruleDao).selectAllRuleParams(dbSession);
}

@Test

+ 9
- 7
server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java View File

@@ -102,7 +102,7 @@ public class RegisterRulesTest {
private static final RuleKey RULE_KEY3 = RuleKey.of("fake", "rule3");
private static final RuleKey HOTSPOT_RULE_KEY = RuleKey.of("fake", "hotspot");

private TestSystem2 system = new TestSystem2().setNow(DATE1.getTime());
private final TestSystem2 system = new TestSystem2().setNow(DATE1.getTime());

@org.junit.Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -113,14 +113,16 @@ public class RegisterRulesTest {
@org.junit.Rule
public LogTester logTester = new LogTester();

private QProfileRules qProfileRules = mock(QProfileRules.class);
private WebServerRuleFinder webServerRuleFinder = mock(WebServerRuleFinder.class);
private DbClient dbClient = db.getDbClient();
private final QProfileRules qProfileRules = mock(QProfileRules.class);
private final WebServerRuleFinder webServerRuleFinder = mock(WebServerRuleFinder.class);
private final DbClient dbClient = db.getDbClient();
private final MetadataIndex metadataIndex = mock(MetadataIndex.class);
private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();

private RuleIndexer ruleIndexer;
private ActiveRuleIndexer activeRuleIndexer;
private RuleIndex ruleIndex;
private MetadataIndex metadataIndex = mock(MetadataIndex.class);
private UuidFactory uuidFactory = UuidFactoryFast.getInstance();


@Before
public void before() {
@@ -996,7 +998,7 @@ public class RegisterRulesTest {
}

@SafeVarargs
private final void createRule(RulesDefinition.Context context, String language, String repositoryKey, String ruleKey, Consumer<NewRule>... consumers) {
private void createRule(RulesDefinition.Context context, String language, String repositoryKey, String ruleKey, Consumer<NewRule>... consumers) {
NewRepository repo = context.createRepository(repositoryKey, language);
NewRule newRule = repo.createRule(ruleKey)
.setName(ruleKey)

+ 3
- 7
server/sonar-webserver-core/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java View File

@@ -45,8 +45,9 @@ public class RegisterMetricsTest {
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);

private UuidFactory uuidFactory = new SequenceUuidFactory();
private DbClient dbClient = dbTester.getDbClient();
private final UuidFactory uuidFactory = new SequenceUuidFactory();
private final DbClient dbClient = dbTester.getDbClient();
private final RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);

/**
* Insert new metrics, including custom metrics
@@ -65,7 +66,6 @@ public class RegisterMetricsTest {
.setUserManaged(true)
.create();

RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
register.register(asList(m1, custom));

Map<String, MetricDto> metricsByKey = selectAllMetrics();
@@ -91,7 +91,6 @@ public class RegisterMetricsTest {
.setDirection(1)
.setHidden(false));

RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
Metric m1 = new Metric.Builder("m1", "New name", Metric.ValueType.FLOAT)
.setDescription("new description")
.setDirection(-1)
@@ -115,7 +114,6 @@ public class RegisterMetricsTest {
IntStream.range(0, count)
.forEach(t -> dbTester.measures().insertMetric(m -> m.setEnabled(random.nextBoolean())));

RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
register.register(Collections.emptyList());

assertThat(selectAllMetrics().values().stream())
@@ -128,7 +126,6 @@ public class RegisterMetricsTest {
MetricDto enabledMetric = dbTester.measures().insertMetric(t -> t.setEnabled(true));
MetricDto disabledMetric = dbTester.measures().insertMetric(t -> t.setEnabled(false));

RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
register.register(asList(builderOf(enabledMetric).create(), builderOf(disabledMetric).create()));

assertThat(selectAllMetrics().values())
@@ -138,7 +135,6 @@ public class RegisterMetricsTest {

@Test
public void insert_core_metrics() {
RegisterMetrics register = new RegisterMetrics(dbClient, uuidFactory);
register.start();

assertThat(dbTester.countRowsOfTable("metrics")).isEqualTo(CoreMetrics.getMetrics().size());

+ 3
- 4
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java View File

@@ -73,8 +73,7 @@ public class RegisterQualityGates implements Startable {
private final UuidFactory uuidFactory;
private final System2 system2;

public RegisterQualityGates(DbClient dbClient,
QualityGateConditionsUpdater qualityGateConditionsUpdater, UuidFactory uuidFactory, System2 system2) {
public RegisterQualityGates(DbClient dbClient, QualityGateConditionsUpdater qualityGateConditionsUpdater, UuidFactory uuidFactory, System2 system2) {
this.dbClient = dbClient;
this.qualityGateConditionsUpdater = qualityGateConditionsUpdater;
this.qualityGateDao = dbClient.qualityGateDao();
@@ -212,7 +211,7 @@ public class RegisterQualityGates implements Startable {
.setQualityGateUuid(qualityGateUuid);
}

// id does not belongs to equals to be able to be compared with builtin
// id does not belong to equals to be able to be compared with builtin
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -227,7 +226,7 @@ public class RegisterQualityGates implements Startable {
Objects.equals(errorThreshold, that.errorThreshold);
}

// id does not belongs to hashcode to be able to be compared with builtin
// id does not belong to hashcode to be able to be compared with builtin
@Override
public int hashCode() {
return Objects.hash(metricKey, operator, errorThreshold);

+ 7
- 2
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java View File

@@ -47,6 +47,8 @@ import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.qualityprofile.RulesProfileDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.DefaultRuleFinder;
import org.sonar.server.rule.ServerRuleFinder;
import org.sonar.server.rule.index.RuleIndex;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.util.TypeValidations;
@@ -62,6 +64,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.api.rules.RulePriority.MAJOR;
@@ -90,7 +93,9 @@ public class RegisterQualityProfilesNotificationTest {
private DbClient dbClient = db.getDbClient();
private TypeValidations typeValidations = mock(TypeValidations.class);
private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, system2, UuidFactoryFast.getInstance(), typeValidations, activeRuleIndexer);
private ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient);
private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, ruleFinder, system2, UuidFactoryFast.getInstance(),
typeValidations, activeRuleIndexer);
private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule);
private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, mock(RuleIndex.class), activeRuleIndexer);
private BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer);
@@ -120,7 +125,7 @@ public class RegisterQualityProfilesNotificationTest {

underTest.start();

verifyZeroInteractions(builtInQualityProfilesNotification);
verifyNoInteractions(builtInQualityProfilesNotification);
}

@Test

+ 3
- 3
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel.java View File

@@ -170,15 +170,15 @@ public abstract class PlatformLevel {
return addIfStandalone;
}

private WebServer getWebServer() {
protected WebServer getWebServer() {
return getOptional(WebServer.class)
.orElseThrow(() -> new IllegalStateException("WebServer not available in Pico yet"));
}

private abstract class AddIf {
protected abstract class AddIf {
private final boolean condition;

private AddIf(boolean condition) {
protected AddIf(boolean condition) {
this.condition = condition;
}


+ 4
- 1
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel3.java View File

@@ -29,6 +29,7 @@ import org.sonar.server.platform.StartupMetadataPersister;
import org.sonar.server.platform.WebCoreExtensionsInstaller;
import org.sonar.server.platform.db.migration.NoopDatabaseMigrationImpl;
import org.sonar.server.platform.serverid.ServerIdModule;
import org.sonar.server.plugins.DetectPluginChange;
import org.sonar.server.setting.DatabaseSettingLoader;
import org.sonar.server.setting.DatabaseSettingsEnabler;

@@ -42,7 +43,9 @@ public class PlatformLevel3 extends PlatformLevel {

@Override
protected void configureLevel() {
addIfStartupLeader(StartupMetadataPersister.class);
addIfStartupLeader(
StartupMetadataPersister.class,
DetectPluginChange.class);
add(
NoopDatabaseMigrationImpl.class,
ServerIdModule.class,

+ 42
- 9
server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java View File

@@ -23,10 +23,12 @@ import org.sonar.api.utils.log.Loggers;
import org.sonar.core.platform.EditionProvider;
import org.sonar.core.platform.PlatformEditionProvider;
import org.sonar.server.app.ProcessCommandWrapper;
import org.sonar.server.authentication.DefaultAdminCredentialsVerifierImpl;
import org.sonar.server.ce.queue.CeQueueCleaner;
import org.sonar.server.es.IndexerStartupTask;
import org.sonar.server.platform.ServerLifecycleNotifier;
import org.sonar.server.platform.web.RegisterServletFilters;
import org.sonar.server.plugins.DetectPluginChange;
import org.sonar.server.plugins.PluginConsentVerifier;
import org.sonar.server.qualitygate.ProjectsInWarningDaemon;
import org.sonar.server.qualitygate.RegisterQualityGates;
@@ -47,6 +49,8 @@ import org.sonar.server.user.DoPrivileged;
import org.sonar.server.user.ThreadLocalUserSession;

public class PlatformLevelStartup extends PlatformLevel {
private AddIfStartupLeaderAndPluginsChanged addIfPluginsChanged;

public PlatformLevelStartup(PlatformLevel parent) {
super("startup tasks", parent);
}
@@ -54,29 +58,57 @@ public class PlatformLevelStartup extends PlatformLevel {
@Override
protected void configureLevel() {
add(GeneratePluginIndex.class,
RegisterPlugins.class,
ServerLifecycleNotifier.class);

addIfStartupLeader(
IndexerStartupTask.class,
IndexerStartupTask.class);
addIfStartupLeaderAndPluginsChanged(
RegisterMetrics.class,
RegisterQualityGates.class,
RegisterRules.class);
add(BuiltInQProfileLoader.class);
RegisterRules.class,
BuiltInQProfileLoader.class);
addIfStartupLeader(
BuiltInQualityProfilesUpdateListener.class,
BuiltInQProfileUpdateImpl.class);
addIfStartupLeaderAndPluginsChanged(
BuiltInQProfileInsertImpl.class,
BuiltInQProfileUpdateImpl.class,
RegisterQualityProfiles.class,
RegisterQualityProfiles.class);
addIfStartupLeader(
RegisterPermissionTemplates.class,
RenameDeprecatedPropertyKeys.class,
CeQueueCleaner.class,
UpgradeSuggestionsCleaner.class,
PluginConsentVerifier.class);
add(RegisterPlugins.class,
// RegisterServletFilters makes the WebService engine of Level4 served by the MasterServletFilter, therefore it
// must be started after all the other startup tasks
RegisterServletFilters.class
);
}

/**
* Add a component to container only if plugins have changed since last start.
*
* @throws IllegalStateException if called from PlatformLevel3 or below, plugin info is loaded yet
*/
AddIfStartupLeaderAndPluginsChanged addIfStartupLeaderAndPluginsChanged(Object... objects) {
if (addIfPluginsChanged == null) {
this.addIfPluginsChanged = new AddIfStartupLeaderAndPluginsChanged(getWebServer().isStartupLeader() && anyPluginChanged());
}
addIfPluginsChanged.ifAdd(objects);
return addIfPluginsChanged;
}

private boolean anyPluginChanged() {
return getOptional(DetectPluginChange.class)
.map(DetectPluginChange::anyPluginChanged)
.orElseThrow(() -> new IllegalStateException("DetectPluginChange not available in Pico yet"));
}

// RegisterServletFilters makes the WebService engine of Level4 served by the MasterServletFilter, therefore it
// must be started after all the other startup tasks
add(RegisterServletFilters.class);
public final class AddIfStartupLeaderAndPluginsChanged extends AddIf {
private AddIfStartupLeaderAndPluginsChanged(boolean condition) {
super(condition);
}
}

@Override
@@ -93,6 +125,7 @@ public class PlatformLevelStartup extends PlatformLevel {
get(WebServerRuleFinder.class).stopCaching();
Loggers.get(PlatformLevelStartup.class)
.info("Running {} Edition", get(PlatformEditionProvider.class).get().map(EditionProvider.Edition::getLabel).orElse(""));
get(DefaultAdminCredentialsVerifierImpl.class).runAtStart();
}
});


Loading…
Cancel
Save