@@ -46,13 +46,8 @@ public class RuleDao implements BatchComponent, ServerComponent { | |||
} | |||
} | |||
public List<RuleDto> selectNonManual() { | |||
SqlSession session = mybatis.openSession(); | |||
try { | |||
return getMapper(session).selectNonManual(); | |||
} finally { | |||
MyBatis.closeQuietly(session); | |||
} | |||
public List<RuleDto> selectNonManual(SqlSession session) { | |||
return getMapper(session).selectNonManual(); | |||
} | |||
public RuleDto selectById(Integer id, SqlSession session) { |
@@ -83,7 +83,7 @@ public class RuleDaoTest extends AbstractDaoTestCase { | |||
@Test | |||
public void testSelectNonManual() throws Exception { | |||
setupData("selectNonManual"); | |||
List<RuleDto> ruleDtos = dao.selectNonManual(); | |||
List<RuleDto> ruleDtos = dao.selectNonManual(getMyBatis().openSession()); | |||
assertThat(ruleDtos.size()).isEqualTo(1); | |||
RuleDto ruleDto = ruleDtos.get(0); |
@@ -47,11 +47,11 @@ public interface RuleDefinitions extends ServerExtension { | |||
private final ListMultimap<String, ExtendedRepository> extendedRepositoriesByKey = ArrayListMultimap.create(); | |||
public NewRepository newRepository(String key, String language) { | |||
return new NewRepositoryImpl(this, key, language); | |||
return new NewRepositoryImpl(this, key, language, false); | |||
} | |||
public NewExtendedRepository extendRepository(String key) { | |||
return new NewRepositoryImpl(this, key); | |||
public NewExtendedRepository extendRepository(String key, String language) { | |||
return new NewRepositoryImpl(this, key, language, true); | |||
} | |||
@CheckForNull | |||
@@ -101,19 +101,13 @@ public interface RuleDefinitions extends ServerExtension { | |||
private String name; | |||
private final Map<String, NewRule> newRules = Maps.newHashMap(); | |||
private NewRepositoryImpl(Context context, String key, String language) { | |||
this.extended = false; | |||
private NewRepositoryImpl(Context context, String key, String language, boolean extended) { | |||
this.extended = extended; | |||
this.context = context; | |||
this.key = this.name = key; | |||
this.language = language; | |||
} | |||
private NewRepositoryImpl(Context context, String key) { | |||
this.extended = true; | |||
this.context = context; | |||
this.key = this.name = key; | |||
} | |||
@Override | |||
public NewRepositoryImpl setName(String s) { | |||
this.name = s; | |||
@@ -146,6 +140,8 @@ public interface RuleDefinitions extends ServerExtension { | |||
static interface ExtendedRepository { | |||
String key(); | |||
String language(); | |||
@CheckForNull | |||
Rule rule(String ruleKey); | |||
@@ -153,8 +149,6 @@ public interface RuleDefinitions extends ServerExtension { | |||
} | |||
static interface Repository extends ExtendedRepository { | |||
String language(); | |||
String name(); | |||
} | |||
@@ -169,7 +163,7 @@ public interface RuleDefinitions extends ServerExtension { | |||
ImmutableMap.Builder<String, Rule> ruleBuilder = ImmutableMap.builder(); | |||
for (NewRule newRule : newRepository.newRules.values()) { | |||
newRule.validate(); | |||
ruleBuilder.put(newRule.key, new Rule(newRule)); | |||
ruleBuilder.put(newRule.key, new Rule(this, newRule)); | |||
} | |||
this.rulesByKey = ruleBuilder.build(); | |||
} | |||
@@ -223,7 +217,8 @@ public interface RuleDefinitions extends ServerExtension { | |||
private final String repoKey, key; | |||
private String name, htmlDescription, metadata, defaultSeverity = Severity.MAJOR; | |||
private boolean template; | |||
private final Set<String> tags = Sets.newHashSet(); | |||
private Status status = Status.READY; | |||
private final Set<String> tags = Sets.newTreeSet(); | |||
private final Map<String, NewParam> paramsByKey = Maps.newHashMap(); | |||
private NewRule(String repoKey, String key) { | |||
@@ -254,6 +249,11 @@ public interface RuleDefinitions extends ServerExtension { | |||
return this; | |||
} | |||
public NewRule setStatus(Status status) { | |||
this.status = status; | |||
return this; | |||
} | |||
public NewParam newParam(String paramKey) { | |||
if (paramsByKey.containsKey(paramKey)) { | |||
throw new IllegalArgumentException(String.format("The parameter '%s' is declared several times on the rule %s", paramKey, this)); | |||
@@ -308,13 +308,20 @@ public interface RuleDefinitions extends ServerExtension { | |||
} | |||
} | |||
static enum Status { | |||
BETA, DEPRECATED, READY | |||
} | |||
static class Rule { | |||
private final Repository repository; | |||
private final String repoKey, key, name, htmlDescription, metadata, defaultSeverity; | |||
private final boolean template; | |||
private final Set<String> tags; | |||
private final Map<String, Param> params; | |||
private final Status status; | |||
private Rule(NewRule newRule) { | |||
private Rule(Repository repository, NewRule newRule) { | |||
this.repository = repository; | |||
this.repoKey = newRule.repoKey; | |||
this.key = newRule.key; | |||
this.name = newRule.name; | |||
@@ -322,7 +329,8 @@ public interface RuleDefinitions extends ServerExtension { | |||
this.metadata = newRule.metadata; | |||
this.defaultSeverity = newRule.defaultSeverity; | |||
this.template = newRule.template; | |||
this.tags = ImmutableSet.copyOf(newRule.tags); | |||
this.status = newRule.status; | |||
this.tags = ImmutableSortedSet.copyOf(newRule.tags); | |||
ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder(); | |||
for (NewParam newParam : newRule.paramsByKey.values()) { | |||
paramsBuilder.put(newParam.key, new Param(newParam)); | |||
@@ -330,6 +338,10 @@ public interface RuleDefinitions extends ServerExtension { | |||
this.params = paramsBuilder.build(); | |||
} | |||
public Repository repository() { | |||
return repository; | |||
} | |||
public String key() { | |||
return key; | |||
} | |||
@@ -351,6 +363,10 @@ public interface RuleDefinitions extends ServerExtension { | |||
return template; | |||
} | |||
public Status status() { | |||
return status; | |||
} | |||
@CheckForNull | |||
public Param param(String key) { | |||
return params.get(key); |
@@ -66,6 +66,7 @@ public class RuleDefinitionsTest { | |||
.setHtmlDescription("Detect <code>java.lang.NullPointerException</code>") | |||
.setDefaultSeverity(Severity.BLOCKER) | |||
.setMetadata("/something") | |||
.setStatus(RuleDefinitions.Status.BETA) | |||
.setTags("one", "two") | |||
.addTags("two", "three", "four"); | |||
newFindbugs.newRule("ABC").setName("ABC").setHtmlDescription("ABC"); | |||
@@ -83,7 +84,9 @@ public class RuleDefinitionsTest { | |||
assertThat(npeRule.params()).isEmpty(); | |||
assertThat(npeRule.metadata()).isEqualTo("/something"); | |||
assertThat(npeRule.template()).isFalse(); | |||
assertThat(npeRule.status()).isEqualTo(RuleDefinitions.Status.BETA); | |||
assertThat(npeRule.toString()).isEqualTo("[repository=findbugs, key=NPE]"); | |||
assertThat(npeRule.repository()).isSameAs(findbugs); | |||
// test equals() and hashCode() | |||
RuleDefinitions.Rule otherRule = findbugs.rule("ABC"); | |||
@@ -102,6 +105,7 @@ public class RuleDefinitionsTest { | |||
assertThat(rule.defaultSeverity()).isEqualTo(Severity.MAJOR); | |||
assertThat(rule.params()).isEmpty(); | |||
assertThat(rule.metadata()).isNull(); | |||
assertThat(rule.status()).isEqualTo(RuleDefinitions.Status.READY); | |||
assertThat(rule.tags()).isEmpty(); | |||
} | |||
@@ -139,7 +143,7 @@ public class RuleDefinitionsTest { | |||
assertThat(context.extendedRepositories()).isEmpty(); | |||
// for example fb-contrib | |||
RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs"); | |||
RuleDefinitions.NewExtendedRepository newFindbugs = context.extendRepository("findbugs", "java"); | |||
newFindbugs.newRule("NPE").setName("NPE").setHtmlDescription("NPE"); | |||
newFindbugs.done(); | |||
@@ -149,6 +153,7 @@ public class RuleDefinitionsTest { | |||
assertThat(context.extendedRepositories("findbugs")).hasSize(1); | |||
RuleDefinitions.ExtendedRepository findbugs = context.extendedRepositories("findbugs").get(0); | |||
assertThat(findbugs.language()).isEqualTo("java"); | |||
assertThat(findbugs.rule("NPE")).isNotNull(); | |||
} | |||
@@ -54,7 +54,7 @@ public class DeprecatedRuleDefinitions implements RuleDefinitions { | |||
newRepository = context.newRepository(repository.getKey(), repository.getLanguage()); | |||
newRepository.setName(repository.getName()); | |||
} else { | |||
newRepository = (NewRepository) context.extendRepository(repository.getKey()); | |||
newRepository = (NewRepository) context.extendRepository(repository.getKey(), repository.getLanguage()); | |||
} | |||
for (org.sonar.api.rules.Rule rule : repository.createRules()) { | |||
// TODO remove org.sonar.api.rules.Rule#tags |
@@ -0,0 +1,48 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.rule; | |||
import org.sonar.api.rule.RuleDefinitions; | |||
/** | |||
* Loads all instances of RuleDefinitions and initializes RuleRepositories. | |||
*/ | |||
public class RuleDefinitionsLoader { | |||
private final RuleDefinitions[] definitions; | |||
private final RuleRepositories repositories; | |||
public RuleDefinitionsLoader(RuleRepositories repositories, RuleDefinitions[] definitions) { | |||
this.repositories = repositories; | |||
this.definitions = definitions; | |||
} | |||
public RuleDefinitionsLoader(RuleRepositories repositories) { | |||
this(repositories, new RuleDefinitions[0]); | |||
} | |||
public RuleDefinitions.Context load() { | |||
RuleDefinitions.Context context = new RuleDefinitions.Context(); | |||
for (RuleDefinitions definition : definitions) { | |||
definition.define(context); | |||
} | |||
repositories.register(context); | |||
return context; | |||
} | |||
} |
@@ -0,0 +1,393 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.rule; | |||
import com.google.common.collect.*; | |||
import org.apache.commons.lang.ObjectUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.apache.ibatis.session.SqlSession; | |||
import org.picocontainer.Startable; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.rule.RuleDefinitions; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RulePriority; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.check.Cardinality; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.qualityprofile.db.ActiveRuleDao; | |||
import org.sonar.core.rule.*; | |||
import org.sonar.server.configuration.ProfilesManager; | |||
import javax.annotation.CheckForNull; | |||
import java.util.*; | |||
public class RuleRegistration implements Startable { | |||
private static final Logger LOG = LoggerFactory.getLogger(RuleRegistration.class); | |||
private final RuleDefinitionsLoader defLoader; | |||
private final ProfilesManager profilesManager; | |||
private final RuleRegistry ruleRegistry; | |||
private final MyBatis myBatis; | |||
private final RuleDao ruleDao; | |||
private final ActiveRuleDao activeRuleDao; | |||
private final System2 system = System2.INSTANCE; | |||
public RuleRegistration(RuleDefinitionsLoader defLoader, ProfilesManager profilesManager, | |||
RuleRegistry ruleRegistry, | |||
MyBatis myBatis, RuleDao ruleDao, ActiveRuleDao activeRuleDao) { | |||
this.defLoader = defLoader; | |||
this.profilesManager = profilesManager; | |||
this.ruleRegistry = ruleRegistry; | |||
this.myBatis = myBatis; | |||
this.ruleDao = ruleDao; | |||
this.activeRuleDao = activeRuleDao; | |||
} | |||
@Override | |||
public void start() { | |||
SqlSession sqlSession = myBatis.openSession(); | |||
try { | |||
Buffer buffer = new Buffer(system.now()); | |||
selectRulesFromDb(buffer, sqlSession); | |||
enableRuleDefinitions(buffer, sqlSession); | |||
processRemainingDbRules(buffer, sqlSession); | |||
index(buffer); | |||
} finally { | |||
sqlSession.close(); | |||
} | |||
} | |||
@Override | |||
public void stop() { | |||
// nothing | |||
} | |||
private void selectRulesFromDb(Buffer buffer, SqlSession sqlSession) { | |||
for (RuleDto ruleDto : ruleDao.selectNonManual(sqlSession)) { | |||
buffer.add(ruleDto); | |||
buffer.markUnprocessed(ruleDto); | |||
} | |||
for (RuleParamDto paramDto : ruleDao.selectParameters(sqlSession)) { | |||
buffer.add(paramDto); | |||
} | |||
for (RuleTagDto tagDto : ruleDao.selectTags(sqlSession)) { | |||
buffer.add(tagDto); | |||
} | |||
} | |||
private void enableRuleDefinitions(Buffer buffer, SqlSession sqlSession) { | |||
RuleDefinitions.Context context = defLoader.load(); | |||
for (RuleDefinitions.Repository repoDef : context.repositories()) { | |||
enableRepository(buffer, sqlSession, repoDef); | |||
} | |||
for (RuleDefinitions.ExtendedRepository extendedRepoDef : context.extendedRepositories()) { | |||
if (context.repository(extendedRepoDef.key())==null) { | |||
LOG.warn(String.format("Extension is ignored, repository %s does not exist", extendedRepoDef.key())); | |||
} else { | |||
enableRepository(buffer, sqlSession, extendedRepoDef); | |||
} | |||
} | |||
} | |||
private void enableRepository(Buffer buffer, SqlSession sqlSession, RuleDefinitions.ExtendedRepository repoDef) { | |||
int count = 0; | |||
for (RuleDefinitions.Rule ruleDef : repoDef.rules()) { | |||
RuleDto dto = buffer.rule(RuleKey.of(ruleDef.repository().key(), ruleDef.key())); | |||
if (dto == null) { | |||
dto = enableAndInsert(buffer, sqlSession, ruleDef); | |||
} else { | |||
enableAndUpdate(buffer, sqlSession, ruleDef, dto); | |||
} | |||
buffer.markProcessed(dto); | |||
count++; | |||
if (count % 100 == 0) { | |||
sqlSession.commit(); | |||
} | |||
} | |||
sqlSession.commit(); | |||
} | |||
private RuleDto enableAndInsert(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef) { | |||
RuleDto ruleDto = new RuleDto() | |||
.setCardinality(ruleDef.template() ? Cardinality.MULTIPLE : Cardinality.SINGLE) | |||
.setConfigKey(ruleDef.metadata()) | |||
.setDescription(ruleDef.htmlDescription()) | |||
.setLanguage(ruleDef.repository().language()) | |||
.setName(ruleDef.name()) | |||
.setRepositoryKey(ruleDef.repository().key()) | |||
.setRuleKey(ruleDef.key()) | |||
.setSeverity(RulePriority.valueOf(ruleDef.defaultSeverity()).ordinal()) | |||
.setCreatedAt(buffer.now()) | |||
.setUpdatedAt(buffer.now()) | |||
.setStatus(ruleDef.status().name()); | |||
ruleDao.insert(ruleDto, sqlSession); | |||
buffer.add(ruleDto); | |||
for (RuleDefinitions.Param param : ruleDef.params()) { | |||
RuleParamDto paramDto = new RuleParamDto() | |||
.setRuleId(ruleDto.getId()) | |||
.setDefaultValue(param.defaultValue()) | |||
.setDescription(param.description()) | |||
.setName(param.name()) | |||
.setType(param.type().toString()); | |||
ruleDao.insert(paramDto, sqlSession); | |||
buffer.add(paramDto); | |||
} | |||
for (String tag : ruleDef.tags()) { | |||
RuleTagDto tagDto = new RuleTagDto().setRuleId(ruleDto.getId()).setTag(tag).setType(RuleTagType.SYSTEM); | |||
ruleDao.insert(tagDto, sqlSession); | |||
buffer.add(tagDto); | |||
} | |||
return ruleDto; | |||
} | |||
private void enableAndUpdate(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto) { | |||
if (mergeRule(buffer, ruleDef, dto)) { | |||
ruleDao.update(dto); | |||
} | |||
mergeParams(buffer, sqlSession, ruleDef, dto); | |||
mergeTags(buffer, sqlSession, ruleDef, dto); | |||
buffer.markProcessed(dto); | |||
} | |||
private boolean mergeRule(Buffer buffer, RuleDefinitions.Rule def, RuleDto dto) { | |||
boolean changed = false; | |||
if (!StringUtils.equals(dto.getName(), def.name())) { | |||
dto.setName(def.name()); | |||
changed = true; | |||
} | |||
if (!StringUtils.equals(dto.getDescription(), def.htmlDescription())) { | |||
dto.setDescription(def.htmlDescription()); | |||
changed = true; | |||
} | |||
if (!StringUtils.equals(dto.getConfigKey(), def.metadata())) { | |||
dto.setConfigKey(def.metadata()); | |||
changed = true; | |||
} | |||
int severity = RulePriority.valueOf(def.defaultSeverity()).ordinal(); | |||
if (!ObjectUtils.equals(dto.getSeverity(), severity)) { | |||
dto.setSeverity(severity); | |||
changed = true; | |||
} | |||
Cardinality cardinality = def.template() ? Cardinality.MULTIPLE : Cardinality.SINGLE; | |||
if (!cardinality.equals(dto.getCardinality())) { | |||
dto.setCardinality(cardinality); | |||
changed = true; | |||
} | |||
String status = def.status().name(); | |||
if (!StringUtils.equals(dto.getStatus(), status)) { | |||
dto.setStatus(status); | |||
changed = true; | |||
} | |||
if (!StringUtils.equals(dto.getLanguage(), def.repository().language())) { | |||
dto.setLanguage(def.repository().language()); | |||
changed = true; | |||
} | |||
if (changed) { | |||
dto.setUpdatedAt(buffer.now()); | |||
} | |||
return changed; | |||
} | |||
private void mergeParams(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto) { | |||
Collection<RuleParamDto> paramDtos = buffer.paramsForRuleId(dto.getId()); | |||
Set<String> persistedParamKeys = Sets.newHashSet(); | |||
for (RuleParamDto paramDto : paramDtos) { | |||
RuleDefinitions.Param paramDef = ruleDef.param(paramDto.getName()); | |||
if (paramDef == null) { | |||
activeRuleDao.deleteParametersWithParamId(paramDto.getId(), sqlSession); | |||
ruleDao.deleteParam(paramDto, sqlSession); | |||
} else { | |||
// TODO validate that existing active rules still match constraints | |||
// TODO store param name | |||
if (mergeParam(paramDto, paramDef)) { | |||
ruleDao.update(paramDto, sqlSession); | |||
} | |||
persistedParamKeys.add(paramDto.getName()); | |||
} | |||
} | |||
for (RuleDefinitions.Param param : ruleDef.params()) { | |||
if (!persistedParamKeys.contains(param.key())) { | |||
RuleParamDto paramDto = new RuleParamDto() | |||
.setRuleId(dto.getId()) | |||
.setName(param.key()) | |||
.setDescription(param.description()) | |||
.setDefaultValue(param.defaultValue()) | |||
.setType(param.type().toString()); | |||
ruleDao.insert(paramDto, sqlSession); | |||
buffer.add(paramDto); | |||
} | |||
} | |||
} | |||
private boolean mergeParam(RuleParamDto paramDto, RuleDefinitions.Param paramDef) { | |||
boolean changed = false; | |||
if (!StringUtils.equals(paramDto.getType(), paramDef.type().toString())) { | |||
paramDto.setType(paramDef.type().toString()); | |||
changed = true; | |||
} | |||
if (!StringUtils.equals(paramDto.getDefaultValue(), paramDef.defaultValue())) { | |||
paramDto.setDefaultValue(paramDef.defaultValue()); | |||
changed = true; | |||
} | |||
if (!StringUtils.equals(paramDto.getDescription(), paramDef.description())) { | |||
paramDto.setDescription(paramDef.description()); | |||
changed = true; | |||
} | |||
return changed; | |||
} | |||
private void mergeTags(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto) { | |||
Set<String> existingSystemTags = Sets.newHashSet(); | |||
Collection<RuleTagDto> tagDtos = ImmutableList.copyOf(buffer.tagsForRuleId(dto.getId())); | |||
for (RuleTagDto tagDto : tagDtos) { | |||
String tag = tagDto.getTag(); | |||
if (tagDto.getType() == RuleTagType.SYSTEM) { | |||
// tag previously declared by plugin | |||
if (!ruleDef.tags().contains(tag)) { | |||
// not declared anymore | |||
ruleDao.deleteTag(tagDto, sqlSession); | |||
buffer.remove(tagDto); | |||
} else { | |||
existingSystemTags.add(tagDto.getTag()); | |||
} | |||
} else { | |||
// tags created by end-users | |||
if (ruleDef.tags().contains(tag)) { | |||
// End-user tag is converted to system tag | |||
ruleDao.deleteTag(tagDto, sqlSession); | |||
buffer.remove(tagDto); | |||
RuleTagDto newTag = new RuleTagDto().setRuleId(dto.getId()).setTag(tag).setType(RuleTagType.SYSTEM); | |||
ruleDao.insert(newTag, sqlSession); | |||
existingSystemTags.add(tag); | |||
buffer.add(newTag); | |||
} | |||
} | |||
} | |||
for (String tag : ruleDef.tags()) { | |||
if (!existingSystemTags.contains(tag)) { | |||
RuleTagDto newTagDto = new RuleTagDto().setRuleId(dto.getId()).setTag(tag).setType(RuleTagType.SYSTEM); | |||
ruleDao.insert(newTagDto, sqlSession); | |||
buffer.add(newTagDto); | |||
} | |||
} | |||
} | |||
private void processRemainingDbRules(Buffer buffer, SqlSession sqlSession) { | |||
List<Integer> removedIds = Lists.newArrayList(); | |||
for (Integer unprocessedRuleId : buffer.unprocessedRuleIds) { | |||
RuleDto ruleDto = buffer.rulesById.get(unprocessedRuleId); | |||
boolean toBeRemoved = true; | |||
if (ruleDto.getParentId() != null && !ruleDto.getStatus().equals(Rule.STATUS_REMOVED)) { | |||
RuleDto parent = buffer.rulesById.get(ruleDto.getParentId()); | |||
if (parent != null) { | |||
// TODO merge params and tags ? | |||
ruleDto.setLanguage(parent.getLanguage()); | |||
ruleDto.setStatus(parent.getStatus()); | |||
ruleDto.setUpdatedAt(buffer.now()); | |||
ruleDao.update(ruleDto, sqlSession); | |||
toBeRemoved = false; | |||
} | |||
} | |||
if (toBeRemoved) { | |||
// TODO log repository key | |||
LOG.info("Disable rule " + ruleDto.getRuleKey()); | |||
ruleDto.setStatus(Rule.STATUS_REMOVED); | |||
ruleDto.setUpdatedAt(buffer.now()); | |||
ruleDao.update(ruleDto, sqlSession); | |||
removedIds.add(ruleDto.getId()); | |||
if (removedIds.size() % 100 == 0) { | |||
sqlSession.commit(); | |||
} | |||
} | |||
} | |||
sqlSession.commit(); | |||
// call to ProfileManager requires session to be committed | |||
for (Integer removedId : removedIds) { | |||
profilesManager.removeActivatedRules(removedId); | |||
} | |||
} | |||
private void index(Buffer buffer) { | |||
ruleRegistry.bulkRegisterRules(buffer.rulesById.values(), buffer.paramsByRuleId, buffer.tagsByRuleId); | |||
} | |||
static class Buffer { | |||
private Date now; | |||
private List<Integer> unprocessedRuleIds = Lists.newArrayList(); | |||
private Map<RuleKey, RuleDto> rulesByKey = Maps.newHashMap(); | |||
private Map<Integer, RuleDto> rulesById = Maps.newHashMap(); | |||
private Multimap<Integer, RuleParamDto> paramsByRuleId = ArrayListMultimap.create(); | |||
private Multimap<Integer, RuleTagDto> tagsByRuleId = ArrayListMultimap.create(); | |||
Buffer(long now) { | |||
this.now = new Date(now); | |||
} | |||
Date now() { | |||
return now; | |||
} | |||
void add(RuleDto rule) { | |||
rulesById.put(rule.getId(), rule); | |||
rulesByKey.put(RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()), rule); | |||
} | |||
void add(RuleParamDto param) { | |||
paramsByRuleId.put(param.getRuleId(), param); | |||
} | |||
void add(RuleTagDto tag) { | |||
tagsByRuleId.put(tag.getRuleId(), tag); | |||
} | |||
void remove(RuleTagDto tag) { | |||
tagsByRuleId.remove(tag.getRuleId(), tag); | |||
} | |||
@CheckForNull | |||
RuleDto rule(RuleKey key) { | |||
return rulesByKey.get(key); | |||
} | |||
Collection<RuleParamDto> paramsForRuleId(Integer ruleId) { | |||
return paramsByRuleId.get(ruleId); | |||
} | |||
Collection<RuleTagDto> tagsForRuleId(Integer ruleId) { | |||
return tagsByRuleId.get(ruleId); | |||
} | |||
void markUnprocessed(RuleDto ruleDto) { | |||
unprocessedRuleIds.add(ruleDto.getId()); | |||
} | |||
void markProcessed(RuleDto ruleDto) { | |||
unprocessedRuleIds.remove(ruleDto.getId()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.rule; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.ImmutableSetMultimap; | |||
import com.google.common.collect.ImmutableSortedSet; | |||
import com.google.common.collect.SetMultimap; | |||
import org.sonar.api.ServerComponent; | |||
import org.sonar.api.rule.RuleDefinitions; | |||
import javax.annotation.CheckForNull; | |||
import java.util.Collection; | |||
import java.util.Map; | |||
import java.util.SortedSet; | |||
/** | |||
* This component keeps metadata of rule repositories. | |||
* <p/> | |||
* Rule repositories are not persisted into datastores, so their metadata (name) | |||
* is kept by this component. | |||
* | |||
* @since 4.2 | |||
*/ | |||
public class RuleRepositories implements ServerComponent { | |||
public static class Repository implements Comparable<Repository> { | |||
private final String key, name, language; | |||
private Repository(RuleDefinitions.Repository repoDef) { | |||
this.key = repoDef.key(); | |||
this.name = repoDef.name(); | |||
this.language = repoDef.language(); | |||
} | |||
public String key() { | |||
return key; | |||
} | |||
public String name() { | |||
return name; | |||
} | |||
public String language() { | |||
return language; | |||
} | |||
/** | |||
* Kept for backward-compatibility in Ruby code | |||
*/ | |||
public String getKey() { | |||
return key; | |||
} | |||
/** | |||
* Kept for backward-compatibility in Ruby code | |||
*/ | |||
public String getName() { | |||
return name; | |||
} | |||
/** | |||
* Kept for backward-compatibility in Ruby code | |||
*/ | |||
public String getLanguage() { | |||
return language; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
Repository that = (Repository) o; | |||
return key.equals(that.key); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return key.hashCode(); | |||
} | |||
@Override | |||
public int compareTo(Repository o) { | |||
return name.toLowerCase().compareTo(o.name.toLowerCase()); | |||
} | |||
} | |||
private SortedSet<Repository> repositories; | |||
private Map<String, Repository> repositoriesByKey; | |||
private SetMultimap<String, Repository> repositoriesByLang; | |||
void register(RuleDefinitions.Context context) { | |||
ImmutableSortedSet.Builder<Repository> listBuilder = ImmutableSortedSet.naturalOrder(); | |||
ImmutableSetMultimap.Builder<String, Repository> langBuilder = ImmutableSetMultimap.builder(); | |||
ImmutableMap.Builder<String, Repository> keyBuilder = ImmutableMap.builder(); | |||
for (RuleDefinitions.Repository repoDef : context.repositories()) { | |||
Repository repository = new Repository(repoDef); | |||
listBuilder.add(repository); | |||
langBuilder.put(repository.language(), repository); | |||
keyBuilder.put(repository.key(), repository); | |||
} | |||
repositories = listBuilder.build(); | |||
repositoriesByLang = langBuilder.build(); | |||
repositoriesByKey = keyBuilder.build(); | |||
} | |||
@CheckForNull | |||
public Repository repository(String key) { | |||
return repositoriesByKey.get(key); | |||
} | |||
/** | |||
* Repositories for a given language, sorted by name. | |||
*/ | |||
public Collection<Repository> repositoriesForLang(String lang) { | |||
return repositoriesByLang.get(lang); | |||
} | |||
/** | |||
* Repositories, sorted by name. | |||
*/ | |||
public Collection<Repository> repositories() { | |||
return repositories; | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.rules; | |||
import com.google.common.collect.HashMultimap; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.SetMultimap; | |||
import org.sonar.api.ServerComponent; | |||
import org.sonar.api.rules.RuleRepository; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
public final class RulesConsole implements ServerComponent { | |||
private List<RuleRepository> repositories = Lists.newArrayList(); | |||
private Map<String, RuleRepository> repositoryByKey = Maps.newHashMap(); | |||
private SetMultimap<String, RuleRepository> repositoriesByLanguage = HashMultimap.create(); | |||
public RulesConsole(RuleRepository[] repositories) { | |||
initRepositories(repositories); | |||
} | |||
private void initRepositories(RuleRepository[] repositories) { | |||
this.repositories.addAll(Arrays.asList(repositories)); | |||
for (RuleRepository repository : this.repositories) { | |||
if (!repositoryByKey.containsKey(repository.getKey())) { | |||
repositoriesByLanguage.put(repository.getLanguage(), repository); | |||
repositoryByKey.put(repository.getKey(), repository); | |||
} | |||
} | |||
} | |||
public Set<RuleRepository> getRepositoriesByLanguage(String language) { | |||
return repositoriesByLanguage.get(language); | |||
} | |||
public List<RuleRepository> getRepositories() { | |||
return repositories; | |||
} | |||
public RuleRepository getRepository(String key) { | |||
return repositoryByKey.get(key); | |||
} | |||
} |
@@ -38,6 +38,7 @@ import org.sonar.core.template.LoadedTemplateDao; | |||
import org.sonar.core.template.LoadedTemplateDto; | |||
import org.sonar.jpa.session.DatabaseSessionFactory; | |||
import org.sonar.server.platform.PersistentSettings; | |||
import org.sonar.server.rule.RuleRegistration; | |||
import org.sonar.server.rule.RuleRegistry; | |||
import java.util.*; | |||
@@ -61,7 +62,7 @@ public class RegisterNewProfiles { | |||
RuleRegistry ruleRegistry, | |||
LoadedTemplateDao loadedTemplateDao, | |||
DatabaseSessionFactory sessionFactory, | |||
RegisterRules registerRulesBefore) { | |||
RuleRegistration registerRulesBefore) { | |||
this.settings = settings; | |||
this.ruleFinder = ruleFinder; | |||
this.ruleRegistry = ruleRegistry; | |||
@@ -75,7 +76,7 @@ public class RegisterNewProfiles { | |||
RuleRegistry ruleRegistry, | |||
LoadedTemplateDao loadedTemplateDao, | |||
DatabaseSessionFactory sessionFactory, | |||
RegisterRules registerRulesBefore) { | |||
RuleRegistration registerRulesBefore) { | |||
this(Collections.<ProfileDefinition>emptyList(), settings, ruleFinder, ruleRegistry, loadedTemplateDao, sessionFactory, registerRulesBefore); | |||
} | |||
@@ -1,457 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.startup; | |||
import com.google.common.base.Objects; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.base.Strings; | |||
import com.google.common.collect.ArrayListMultimap; | |||
import com.google.common.collect.Iterables; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.Multimap; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.apache.ibatis.session.SqlSession; | |||
import org.elasticsearch.common.collect.Lists; | |||
import org.elasticsearch.common.collect.Sets; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RuleParam; | |||
import org.sonar.api.rules.RuleRepository; | |||
import org.sonar.api.utils.SonarException; | |||
import org.sonar.api.utils.TimeProfiler; | |||
import org.sonar.core.i18n.RuleI18nManager; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.qualityprofile.db.ActiveRuleDao; | |||
import org.sonar.core.rule.*; | |||
import org.sonar.server.configuration.ProfilesManager; | |||
import org.sonar.server.rule.RuleRegistry; | |||
import java.util.*; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static com.google.common.collect.Maps.newHashMap; | |||
import static com.google.common.collect.Sets.newHashSet; | |||
public final class RegisterRules { | |||
private static final Logger LOG = LoggerFactory.getLogger(RegisterRules.class); | |||
private final ProfilesManager profilesManager; | |||
private final List<RuleRepository> repositories; | |||
private final RuleI18nManager ruleI18nManager; | |||
private final RuleRegistry ruleRegistry; | |||
private final MyBatis myBatis; | |||
private final RuleDao ruleDao; | |||
private final ActiveRuleDao activeRuleDao; | |||
private SqlSession sqlSession; | |||
public RegisterRules(RuleRepository[] repos, RuleI18nManager ruleI18nManager, ProfilesManager profilesManager, RuleRegistry ruleRegistry, | |||
MyBatis myBatis, RuleDao ruleDao, ActiveRuleDao activeRuleDao) { | |||
this.profilesManager = profilesManager; | |||
this.repositories = newArrayList(repos); | |||
this.ruleI18nManager = ruleI18nManager; | |||
this.ruleRegistry = ruleRegistry; | |||
this.myBatis = myBatis; | |||
this.ruleDao = ruleDao; | |||
this.activeRuleDao = activeRuleDao; | |||
} | |||
public RegisterRules(RuleI18nManager ruleI18nManager, ProfilesManager profilesManager, RuleRegistry ruleRegistry, MyBatis myBatis, | |||
RuleDao ruleDao, ActiveRuleDao activeRuleDao) { | |||
this(new RuleRepository[0], ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
} | |||
public void start() { | |||
sqlSession = myBatis.openSession(); | |||
RulesByRepository existingRules = new RulesByRepository( | |||
findAllRules(), ruleDao.selectParameters(sqlSession), ruleDao.selectTags(sqlSession)); | |||
try { | |||
List<RuleDto> registeredRules = registerRules(existingRules); | |||
LOG.info("Removing deprecated rules"); | |||
Set<RuleDto> disabledRules = newHashSet(); | |||
disabledRules.addAll(disableDeprecatedRules(existingRules, registeredRules)); | |||
disabledRules.addAll(disableDeprecatedRepositories(existingRules)); | |||
sqlSession.commit(); | |||
removeActiveRulesOnDisabledRules(disabledRules); | |||
Set<RuleDto> allRules = Sets.newHashSet(); | |||
allRules.addAll(existingRules.rules()); | |||
allRules.addAll(registeredRules); | |||
ruleRegistry.bulkRegisterRules(allRules, existingRules.params, existingRules.tags); | |||
} finally { | |||
sqlSession.close(); | |||
} | |||
} | |||
private List<RuleDto> findAllRules() { | |||
return ruleDao.selectNonManual(); | |||
} | |||
private List<RuleDto> registerRules(RulesByRepository existingRules) { | |||
TimeProfiler profiler = new TimeProfiler(); | |||
List<RuleDto> registeredRules = newArrayList(); | |||
for (RuleRepository repository : repositories) { | |||
profiler.start("Register rules [" + repository.getKey() + "/" + StringUtils.defaultString(repository.getLanguage(), "-") + "]"); | |||
registeredRules.addAll(registerRepositoryRules(repository, existingRules)); | |||
profiler.stop(); | |||
} | |||
// Template rules have to be registered after all rules in order for their parent to be updated. | |||
registeredRules.addAll(registerTemplateRules(registeredRules, existingRules)); | |||
return registeredRules; | |||
} | |||
private List<RuleDto> registerRepositoryRules(RuleRepository repository, RulesByRepository existingRules) { | |||
List<RuleDto> registeredRules = newArrayList(); | |||
Map<String, Rule> ruleByKey = newHashMap(); | |||
for (Rule rule : repository.createRules()) { | |||
updateRuleFromRepositoryInfo(rule, repository); | |||
validateRule(rule, repository.getKey()); | |||
ruleByKey.put(rule.getKey(), rule); | |||
} | |||
LOG.debug(ruleByKey.size() + " rules"); | |||
for (RuleDto persistedRule : existingRules.get(repository.getKey())) { | |||
Rule rule = ruleByKey.get(persistedRule.getRuleKey()); | |||
if (rule != null) { | |||
updateExistingRule(persistedRule, existingRules, rule); | |||
ruleDao.update(persistedRule, sqlSession); | |||
registeredRules.add(persistedRule); | |||
ruleByKey.remove(rule.getKey()); | |||
} | |||
} | |||
registeredRules.addAll(saveNewRules(ruleByKey.values(), existingRules)); | |||
return registeredRules; | |||
} | |||
/** | |||
* Template rules do not exists in rule repositories, only in database, they have to be updated from their parent. | |||
*/ | |||
private List<RuleDto> registerTemplateRules(List<RuleDto> registeredRules, RulesByRepository existingRules) { | |||
List<RuleDto> templateRules = newArrayList(); | |||
for (RuleDto persistedRule : existingRules.rules()) { | |||
RuleDto parent = existingRules.ruleById(persistedRule.getParentId()); | |||
if (parent != null && registeredRules.contains(parent)) { | |||
persistedRule.setRepositoryKey(parent.getRepositoryKey()); | |||
persistedRule.setLanguage(parent.getLanguage()); | |||
persistedRule.setStatus(Objects.firstNonNull(persistedRule.getStatus(), Rule.STATUS_READY)); | |||
persistedRule.setCreatedAt(Objects.firstNonNull(persistedRule.getCreatedAt(), new Date())); | |||
persistedRule.setUpdatedAt(new Date()); | |||
ruleDao.update(persistedRule, sqlSession); | |||
templateRules.add(persistedRule); | |||
} | |||
} | |||
return templateRules; | |||
} | |||
private void updateRuleFromRepositoryInfo(Rule rule, RuleRepository repository) { | |||
rule.setRepositoryKey(repository.getKey()); | |||
rule.setLanguage(repository.getLanguage()); | |||
rule.setStatus(Objects.firstNonNull(rule.getStatus(), Rule.STATUS_READY)); | |||
} | |||
private void validateRule(Rule rule, String repositoryKey) { | |||
validateRuleRepositoryName(rule, repositoryKey); | |||
validateRuleDescription(rule, repositoryKey); | |||
} | |||
private void validateRuleRepositoryName(Rule rule, String repositoryKey) { | |||
String nameFromBundle = ruleI18nManager.getName(repositoryKey, rule.getKey()); | |||
if (!Strings.isNullOrEmpty(nameFromBundle)) { | |||
rule.setName(nameFromBundle); | |||
} | |||
if (Strings.isNullOrEmpty(rule.getName())) { | |||
throw new SonarException("The following rule (repository: " + repositoryKey + ") must have a name: " + rule); | |||
} | |||
} | |||
private void validateRuleDescription(Rule rule, String repositoryKey) { | |||
String descriptionFromBundle = ruleI18nManager.getDescription(repositoryKey, rule.getKey()); | |||
if (!Strings.isNullOrEmpty(descriptionFromBundle)) { | |||
rule.setDescription(descriptionFromBundle); | |||
} | |||
if (Strings.isNullOrEmpty(rule.getDescription())) { | |||
if (!Strings.isNullOrEmpty(rule.getName()) && Strings.isNullOrEmpty(ruleI18nManager.getName(repositoryKey, rule.getKey()))) { | |||
// specific case | |||
throw new SonarException("No description found for the rule '" + rule.getName() + "' (repository: " + repositoryKey + ") because the entry 'rule." | |||
+ repositoryKey + "." + rule.getKey() + ".name' is missing from the bundle."); | |||
} else { | |||
throw new SonarException("The following rule (repository: " + repositoryKey + ") must have a description: " + rule); | |||
} | |||
} | |||
} | |||
private void updateExistingRule(RuleDto persistedRule, RulesByRepository existingRules, Rule rule) { | |||
LOG.debug("Update existing rule " + rule); | |||
persistedRule.setName(rule.getName()); | |||
persistedRule.setConfigKey(rule.getConfigKey()); | |||
persistedRule.setDescription(rule.getDescription()); | |||
persistedRule.setSeverity(rule.getSeverity().ordinal()); | |||
persistedRule.setCardinality(rule.getCardinality()); | |||
persistedRule.setStatus(rule.getStatus()); | |||
persistedRule.setLanguage(rule.getLanguage()); | |||
persistedRule.setUpdatedAt(new Date()); | |||
Collection<RuleParamDto> ruleParams = existingRules.params(persistedRule.getId()); | |||
// delete deprecated params | |||
deleteDeprecatedParameters(ruleParams, rule); | |||
// add new params and update existing params | |||
updateParameters(persistedRule, ruleParams, rule); | |||
synchronizeTags(persistedRule, existingRules, rule); | |||
} | |||
private void updateParameters(RuleDto persistedRule, Collection<RuleParamDto> ruleParams, Rule rule) { | |||
Map<String, RuleParamDto> paramsByKey = Maps.newHashMap(); | |||
for (RuleParamDto param: ruleParams) { | |||
paramsByKey.put(param.getName(), param); | |||
} | |||
if (rule.getParams() != null) { | |||
for (RuleParam param : rule.getParams()) { | |||
RuleParamDto persistedParam = paramsByKey.get(param.getKey()); | |||
if (persistedParam == null) { | |||
persistedParam = new RuleParamDto() | |||
.setRuleId(persistedRule.getId()) | |||
.setName(param.getKey()) | |||
.setType(param.getType()); | |||
ruleDao.insert(persistedParam, sqlSession); | |||
ruleParams.add(persistedParam); | |||
} | |||
String desc = StringUtils.defaultIfEmpty( | |||
ruleI18nManager.getParamDescription(rule.getRepositoryKey(), rule.getKey(), param.getKey()), | |||
param.getDescription() | |||
); | |||
persistedParam.setDescription(desc); | |||
persistedParam.setType(param.getType()); | |||
persistedParam.setDefaultValue(param.getDefaultValue()); | |||
ruleDao.update(persistedParam, sqlSession); | |||
} | |||
} | |||
} | |||
private void deleteDeprecatedParameters(Collection<RuleParamDto> ruleParams, Rule rule) { | |||
if (ruleParams != null && !ruleParams.isEmpty()) { | |||
for (Iterator<RuleParamDto> it = ruleParams.iterator(); it.hasNext(); ) { | |||
RuleParamDto persistedParam = it.next(); | |||
if (rule.getParam(persistedParam.getName()) == null) { | |||
it.remove(); | |||
activeRuleDao.deleteParametersWithParamId(persistedParam.getId(), sqlSession); | |||
ruleDao.deleteParam(persistedParam, sqlSession); | |||
} | |||
} | |||
} | |||
} | |||
void synchronizeTags(RuleDto persistedRule, RulesByRepository existingRules, Rule rule) { | |||
Set<String> existingSystemTags = Sets.newHashSet(); | |||
List<RuleTagDto> newTags = Lists.newArrayList(); | |||
Iterator<RuleTagDto> tagsIterator = existingRules.tags(persistedRule.getId()).iterator(); | |||
while (tagsIterator.hasNext()) { | |||
RuleTagDto existingTag = tagsIterator.next(); | |||
String existingTagValue = existingTag.getTag(); | |||
if (existingTag.getType() == RuleTagType.SYSTEM) { | |||
existingSystemTags.add(existingTagValue); | |||
if (! rule.getTags().contains(existingTagValue)) { | |||
ruleDao.deleteTag(existingTag, sqlSession); | |||
tagsIterator.remove(); | |||
} | |||
} else { | |||
if (rule.getTags().contains(existingTagValue)) { | |||
// Existing admin tag with same value as system tag must be converted | |||
ruleDao.deleteTag(existingTag, sqlSession); | |||
tagsIterator.remove(); | |||
existingSystemTags.add(existingTagValue); | |||
RuleTagDto newTag = dtoFrom(existingTagValue, persistedRule.getId()); | |||
ruleDao.insert(newTag, sqlSession); | |||
newTags.add(newTag); | |||
} | |||
} | |||
} | |||
for (String newTag: rule.getTags()) { | |||
if (! existingSystemTags.contains(newTag)) { | |||
RuleTagDto newTagDto = dtoFrom(newTag, persistedRule.getId()); | |||
ruleDao.insert(newTagDto, sqlSession); | |||
newTags.add(newTagDto); | |||
} | |||
} | |||
existingRules.tags.putAll(persistedRule.getId(), newTags); | |||
} | |||
private List<RuleDto> saveNewRules(Collection<Rule> rules, RulesByRepository existingRules) { | |||
List<RuleDto> registeredRules = newArrayList(); | |||
for (Rule rule : rules) { | |||
LOG.debug("Save new rule " + rule); | |||
RuleDto newRule = dtoFrom(rule); | |||
newRule.setCreatedAt(new Date()); | |||
ruleDao.insert(newRule, sqlSession); | |||
registeredRules.add(newRule); | |||
for(RuleParam param : rule.getParams()) { | |||
RuleParamDto newParam = dtoFrom(param, newRule.getId()); | |||
ruleDao.insert(newParam, sqlSession); | |||
existingRules.params.put(newRule.getId(), newParam); | |||
} | |||
for(String tag : rule.getTags()) { | |||
RuleTagDto newTag = dtoFrom(tag, newRule.getId()); | |||
ruleDao.insert(newTag, sqlSession); | |||
existingRules.tags.put(newRule.getId(), newTag); | |||
} | |||
} | |||
return registeredRules; | |||
} | |||
private Collection<RuleDto> disableDeprecatedRules(RulesByRepository existingRules, List<RuleDto> registeredRules) { | |||
List<RuleDto> disabledRules = newArrayList(); | |||
for (RuleDto rule : existingRules.rules()) { | |||
if (!registeredRules.contains(rule)) { | |||
disableRule(rule); | |||
disabledRules.add(rule); | |||
} | |||
} | |||
return disabledRules; | |||
} | |||
private Collection<RuleDto> disableDeprecatedRepositories(RulesByRepository existingRules) { | |||
List<RuleDto> disabledRules = newArrayList(); | |||
for (final String repositoryKey : existingRules.repositories()) { | |||
if (!Iterables.any(repositories, new Predicate<RuleRepository>() { | |||
public boolean apply(RuleRepository input) { | |||
return input.getKey().equals(repositoryKey); | |||
} | |||
})) { | |||
for (RuleDto rule : existingRules.get(repositoryKey)) { | |||
disableRule(rule); | |||
disabledRules.add(rule); | |||
} | |||
} | |||
} | |||
return disabledRules; | |||
} | |||
private void disableRule(RuleDto rule) { | |||
if (!rule.getStatus().equals(Rule.STATUS_REMOVED)) { | |||
LOG.info("Removing rule " + rule.getRuleKey()); | |||
rule.setStatus(Rule.STATUS_REMOVED); | |||
rule.setUpdatedAt(new Date()); | |||
ruleDao.update(rule, sqlSession); | |||
} | |||
} | |||
private void removeActiveRulesOnDisabledRules(Set<RuleDto> disabledRules) { | |||
for (RuleDto rule : disabledRules) { | |||
profilesManager.removeActivatedRules(rule.getId()); | |||
} | |||
} | |||
static RuleDto dtoFrom(Rule rule) { | |||
return new RuleDto() | |||
.setCardinality(rule.getCardinality()) | |||
.setConfigKey(rule.getConfigKey()) | |||
.setDescription(rule.getDescription()) | |||
.setLanguage(rule.getLanguage()) | |||
.setName(rule.getName()) | |||
.setRepositoryKey(rule.getRepositoryKey()) | |||
.setRuleKey(rule.getKey()) | |||
.setSeverity(rule.getSeverity().ordinal()) | |||
.setStatus(rule.getStatus()); | |||
} | |||
static RuleParamDto dtoFrom(RuleParam param, Integer ruleId) { | |||
return new RuleParamDto() | |||
.setRuleId(ruleId) | |||
.setDefaultValue(param.getDefaultValue()) | |||
.setDescription(param.getDescription()) | |||
.setName(param.getKey()) | |||
.setType(param.getType()); | |||
} | |||
static RuleTagDto dtoFrom(String tag, Integer ruleId) { | |||
return new RuleTagDto() | |||
.setRuleId(ruleId) | |||
.setTag(tag) | |||
.setType(RuleTagType.SYSTEM); | |||
} | |||
static class RulesByRepository { | |||
Multimap<String, RuleDto> ruleRepositoryList; | |||
Map<Integer, RuleDto> rulesById; | |||
Multimap<Integer, RuleParamDto> params; | |||
Multimap<Integer, RuleTagDto> tags; | |||
RulesByRepository(List<RuleDto> rules, List<RuleParamDto> params, List<RuleTagDto> tags) { | |||
ruleRepositoryList = ArrayListMultimap.create(); | |||
rulesById = Maps.newHashMap(); | |||
for (RuleDto rule : rules) { | |||
ruleRepositoryList.put(rule.getRepositoryKey(), rule); | |||
rulesById.put(rule.getId(), rule); | |||
} | |||
this.params = ArrayListMultimap.create(); | |||
for (RuleParamDto param: params) { | |||
this.params.put(param.getRuleId(), param); | |||
} | |||
this.tags = ArrayListMultimap.create(); | |||
for (RuleTagDto tag: tags) { | |||
this.tags.put(tag.getRuleId(), tag); | |||
} | |||
} | |||
Collection<RuleDto> get(String repositoryKey) { | |||
return ruleRepositoryList.get(repositoryKey); | |||
} | |||
Collection<String> repositories() { | |||
return ruleRepositoryList.keySet(); | |||
} | |||
Collection<RuleDto> rules() { | |||
return ruleRepositoryList.values(); | |||
} | |||
RuleDto ruleById(Integer id) { | |||
return rulesById.get(id); | |||
} | |||
Collection<RuleParamDto> params(Integer ruleId) { | |||
return params.get(ruleId); | |||
} | |||
Collection<RuleTagDto> tags(Integer ruleId) { | |||
return tags.get(ruleId); | |||
} | |||
} | |||
} |
@@ -26,6 +26,7 @@ import org.sonar.api.utils.TimeProfiler; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelSynchronizer; | |||
import org.sonar.core.technicaldebt.TechnicalDebtRuleCache; | |||
import org.sonar.server.rule.RuleRegistration; | |||
public final class RegisterTechnicalDebtModel { | |||
@@ -37,7 +38,7 @@ public final class RegisterTechnicalDebtModel { | |||
/** | |||
* @param registerRulesBeforeModels used only to be started after the creation of check templates | |||
*/ | |||
public RegisterTechnicalDebtModel(TechnicalDebtModelSynchronizer manager, RuleFinder ruleFinder, RegisterRules registerRulesBeforeModels) { | |||
public RegisterTechnicalDebtModel(TechnicalDebtModelSynchronizer manager, RuleFinder ruleFinder, RuleRegistration registerRulesBeforeModels) { | |||
this.manager = manager; | |||
this.ruleFinder = ruleFinder; | |||
} |
@@ -34,7 +34,6 @@ import org.sonar.api.resources.Language; | |||
import org.sonar.api.resources.ResourceType; | |||
import org.sonar.api.resources.ResourceTypes; | |||
import org.sonar.api.rules.RulePriority; | |||
import org.sonar.api.rules.RuleRepository; | |||
import org.sonar.api.test.MutableTestPlan; | |||
import org.sonar.api.test.MutableTestable; | |||
import org.sonar.api.test.TestPlan; | |||
@@ -58,18 +57,20 @@ import org.sonar.server.platform.ServerIdGenerator; | |||
import org.sonar.server.platform.ServerSettings; | |||
import org.sonar.server.platform.SettingsChangeNotifier; | |||
import org.sonar.server.plugins.*; | |||
import org.sonar.server.rule.RuleRepositories; | |||
import org.sonar.server.rules.ProfilesConsole; | |||
import org.sonar.server.rules.RulesConsole; | |||
import org.sonar.server.user.NewUserNotifier; | |||
import org.sonar.updatecenter.common.PluginReferential; | |||
import org.sonar.updatecenter.common.UpdateCenter; | |||
import org.sonar.updatecenter.common.Version; | |||
import javax.annotation.Nullable; | |||
import java.net.InetAddress; | |||
import java.sql.Connection; | |||
import java.util.*; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
@@ -245,16 +246,16 @@ public final class JRubyFacade { | |||
} | |||
/* PROFILES CONSOLE : RULES AND METRIC THRESHOLDS */ | |||
public List<RuleRepository> getRuleRepositories() { | |||
return get(RulesConsole.class).getRepositories(); | |||
public Collection<RuleRepositories.Repository> getRuleRepositories() { | |||
return get(RuleRepositories.class).repositories(); | |||
} | |||
public RuleRepository getRuleRepository(String repositoryKey) { | |||
return get(RulesConsole.class).getRepository(repositoryKey); | |||
public RuleRepositories.Repository getRuleRepository(String repositoryKey) { | |||
return get(RuleRepositories.class).repository(repositoryKey); | |||
} | |||
public Set<RuleRepository> getRuleRepositoriesByLanguage(String languageKey) { | |||
return get(RulesConsole.class).getRepositoriesByLanguage(languageKey); | |||
public Collection<RuleRepositories.Repository> getRuleRepositoriesByLanguage(String languageKey) { | |||
return get(RuleRepositories.class).repositoriesForLang(languageKey); | |||
} | |||
// TODO move this to QProfiles | |||
@@ -303,7 +304,7 @@ public final class JRubyFacade { | |||
public void ruleSeverityChanged(int parentProfileId, int activeRuleId, int oldSeverityId, int newSeverityId, String userName) { | |||
getProfilesManager().ruleSeverityChanged(parentProfileId, activeRuleId, RulePriority.values()[oldSeverityId], | |||
RulePriority.values()[newSeverityId], userName); | |||
RulePriority.values()[newSeverityId], userName); | |||
} | |||
public void ruleDeactivated(int parentProfileId, int deactivatedRuleId, String userName) { | |||
@@ -457,10 +458,10 @@ public final class JRubyFacade { | |||
// notifier is null when creating the administrator in the migration script 011. | |||
if (notifier != null) { | |||
notifier.onNewUser(NewUserHandler.Context.builder() | |||
.setLogin(fields.get("login")) | |||
.setName(fields.get("name")) | |||
.setEmail(fields.get("email")) | |||
.build()); | |||
.setLogin(fields.get("login")) | |||
.setName(fields.get("name")) | |||
.setEmail(fields.get("email")) | |||
.build()); | |||
} | |||
} | |||
@@ -38,7 +38,7 @@ class RulesConfigurationController < ApplicationController | |||
init_params() | |||
@select_repositories = ANY_SELECTION + java_facade.getRuleRepositoriesByLanguage(@profile.language).collect { |repo| [repo.getName(true), repo.getKey()] }.sort | |||
@select_repositories = ANY_SELECTION + java_facade.getRuleRepositoriesByLanguage(@profile.language).collect { |repo| [repo.name(), repo.key()] }.sort | |||
@select_priority = ANY_SELECTION + RULE_PRIORITIES | |||
@select_activation = [[message('any'), 'any'], [message('active'), STATUS_ACTIVE], [message('inactive'), STATUS_INACTIVE]] | |||
@select_inheritance = [[message('any'), 'any'], [message('rules_configuration.not_inherited'), 'NOT'], [message('rules_configuration.inherited'), 'INHERITED'], |
@@ -0,0 +1,77 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.rule; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleDefinitions; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class RuleDefinitionsLoaderTest { | |||
@Test | |||
public void no_definitions() { | |||
RuleRepositories repositories = new RuleRepositories(); | |||
RuleDefinitions.Context context = new RuleDefinitionsLoader(repositories).load(); | |||
assertThat(context.repositories()).isEmpty(); | |||
assertThat(repositories.repositories()).isEmpty(); | |||
} | |||
@Test | |||
public void load_definitions() { | |||
RuleRepositories repositories = new RuleRepositories(); | |||
RuleDefinitions.Context context = new RuleDefinitionsLoader(repositories, new RuleDefinitions[]{ | |||
new FindbugsDefinitions(), new SquidDefinitions() | |||
}).load(); | |||
assertThat(context.repositories()).hasSize(2); | |||
assertThat(context.repository("findbugs")).isNotNull(); | |||
assertThat(context.repository("squid")).isNotNull(); | |||
assertThat(repositories.repositories()).hasSize(2); | |||
assertThat(repositories.repository("findbugs")).isNotNull(); | |||
assertThat(repositories.repository("squid")).isNotNull(); | |||
} | |||
static class FindbugsDefinitions implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("findbugs", "java"); | |||
repo.setName("Findbugs"); | |||
repo.newRule("ABC") | |||
.setName("ABC") | |||
.setHtmlDescription("Description of ABC"); | |||
repo.done(); | |||
} | |||
} | |||
static class SquidDefinitions implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("squid", "java"); | |||
repo.setName("Squid"); | |||
repo.newRule("DEF") | |||
.setName("DEF") | |||
.setHtmlDescription("Description of DEF"); | |||
repo.done(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,263 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.rule; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleDefinitions; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.core.persistence.AbstractDaoTestCase; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.qualityprofile.db.ActiveRuleDao; | |||
import org.sonar.core.rule.RuleDao; | |||
import org.sonar.server.configuration.ProfilesManager; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
public class RuleRegistrationTest extends AbstractDaoTestCase { | |||
private static final String[] EXCLUDED_COLUMN_NAMES = { | |||
"created_at", "updated_at", "note_data", "note_user_login", "note_created_at", "note_updated_at"}; | |||
RuleRegistration task; | |||
ProfilesManager profilesManager; | |||
RuleRegistry ruleRegistry; | |||
MyBatis myBatis; | |||
RuleDao ruleDao; | |||
ActiveRuleDao activeRuleDao; | |||
@Before | |||
public void before() { | |||
profilesManager = mock(ProfilesManager.class); | |||
ruleRegistry = mock(RuleRegistry.class); | |||
myBatis = getMyBatis(); | |||
ruleDao = new RuleDao(myBatis); | |||
activeRuleDao = new ActiveRuleDao(myBatis); | |||
task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{new FakeRepository()}), | |||
profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
} | |||
@Test | |||
public void should_insert_new_rules() { | |||
setupData("shared"); | |||
task.start(); | |||
checkTables("should_insert_new_rules", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters", "rule_tags"); | |||
} | |||
@Test | |||
public void should_update_template_rule_language() { | |||
setupData("should_update_template_rule_language"); | |||
task.start(); | |||
checkTables("should_update_template_rule_language", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_notify_for_removed_rules() { | |||
setupData("shared"); | |||
task.start(); | |||
verify(profilesManager).removeActivatedRules(1); | |||
} | |||
@Test | |||
public void should_reactivate_disabled_rules() { | |||
setupData("should_reactivate_disabled_rules"); | |||
task.start(); | |||
checkTables("should_reactivate_disabled_rules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
assertThat(ruleDao.selectById(1).getUpdatedAt()).isNotNull(); | |||
} | |||
@Test | |||
public void should_not_reactivate_disabled_template_rules() { | |||
setupData("should_not_reactivate_disabled_template_rules"); | |||
task.start(); | |||
checkTables("should_not_reactivate_disabled_template_rules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_disable_deprecated_active_rules() { | |||
setupData("should_disable_deprecated_active_rules"); | |||
task.start(); | |||
checkTables("should_disable_deprecated_active_rules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_disable_deprecated_active_rule_params() { | |||
setupData("should_disable_deprecated_active_rule_params"); | |||
task.start(); | |||
checkTables("should_disable_deprecated_active_rule_params", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters", "active_rules", "active_rule_parameters"); | |||
} | |||
@Test | |||
public void should_disable_deprecated_rules() { | |||
setupData("should_disable_deprecated_rules"); | |||
task.start(); | |||
checkTables("should_disable_deprecated_rules", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters"); | |||
} | |||
@Test | |||
public void should_update_rule_fields() { | |||
setupData("updadeRuleFields"); | |||
task.start(); | |||
checkTables("updadeRuleFields", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters", "rule_tags"); | |||
} | |||
@Test | |||
public void should_update_rule_parameters() { | |||
setupData("should_update_rule_parameters"); | |||
task.start(); | |||
checkTables("should_update_rule_parameters", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters"); | |||
} | |||
@Test | |||
public void should_not_disable_template_rules_if_parent_is_enabled() { | |||
setupData("doNotDisableUserRulesIfParentIsEnabled"); | |||
task.start(); | |||
checkTables("doNotDisableUserRulesIfParentIsEnabled", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_disable_template_rules_if_parent_is_disabled() { | |||
setupData("disableUserRulesIfParentIsDisabled"); | |||
task.start(); | |||
checkTables("disableUserRulesIfParentIsDisabled", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_not_disable_manual_rules() { | |||
// the hardcoded repository "manual" is used for manual violations | |||
setupData("shouldNotDisableManualRules"); | |||
task.start(); | |||
checkTables("shouldNotDisableManualRules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void test_high_number_of_rules() { | |||
task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{new BigRepository()}), | |||
profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("shared"); | |||
task.start(); | |||
// There is already one rule in DB | |||
assertThat(ruleDao.selectAll()).hasSize(BigRepository.SIZE + 1); | |||
assertThat(ruleDao.selectParameters()).hasSize(BigRepository.SIZE * 20); | |||
assertThat(ruleDao.selectTags(getMyBatis().openSession())).hasSize(BigRepository.SIZE * 3); | |||
} | |||
@Test | |||
public void should_insert_extended_repositories() { | |||
task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{ | |||
new FindbugsRepository(), new FbContribRepository()}), | |||
profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("empty"); | |||
task.start(); | |||
checkTables("should_insert_extended_repositories", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
static class FakeRepository implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("fake", "java"); | |||
NewRule rule1 = repo.newRule("rule1") | |||
.setName("One") | |||
.setHtmlDescription("Description of One") | |||
.setDefaultSeverity(Severity.BLOCKER) | |||
.setMetadata("config1") | |||
.setTags("tag1", "tag3", "tag5"); | |||
rule1.newParam("param1").setDescription("parameter one").setDefaultValue("default value one"); | |||
rule1.newParam("param2").setDescription("parameter two").setDefaultValue("default value two"); | |||
repo.newRule("rule2") | |||
.setName("Two") | |||
.setHtmlDescription("Description of Two") | |||
.setDefaultSeverity(Severity.INFO) | |||
.setStatus(Status.DEPRECATED); | |||
repo.done(); | |||
} | |||
} | |||
static class BigRepository implements RuleDefinitions { | |||
static final int SIZE = 500; | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("big", "java"); | |||
for (int i = 0; i < SIZE; i++) { | |||
NewRule rule = repo.newRule("rule" + i) | |||
.setName("name of " + i) | |||
.setHtmlDescription("description of " + i) | |||
.setDefaultSeverity(Severity.BLOCKER) | |||
.setMetadata("config1") | |||
.setTags("tag1", "tag3", "tag5"); | |||
for (int j = 0; j < 20; j++) { | |||
rule.newParam("param" + j); | |||
} | |||
} | |||
repo.done(); | |||
} | |||
} | |||
static class FindbugsRepository implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("findbugs", "java"); | |||
repo.newRule("rule1") | |||
.setName("Rule One") | |||
.setHtmlDescription("Description of Rule One"); | |||
repo.done(); | |||
} | |||
} | |||
static class FbContribRepository implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewExtendedRepository repo = context.extendRepository("findbugs", "java"); | |||
repo.newRule("rule2") | |||
.setName("Rule Two") | |||
.setHtmlDescription("Description of Rule Two"); | |||
repo.done(); | |||
} | |||
} | |||
} | |||
@@ -58,6 +58,7 @@ import static org.elasticsearch.index.query.FilterBuilders.hasChildFilter; | |||
import static org.elasticsearch.index.query.FilterBuilders.hasParentFilter; | |||
import static org.elasticsearch.index.query.FilterBuilders.termFilter; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -261,7 +262,7 @@ public class RuleRegistryTest { | |||
List<RuleDto> rules = ImmutableList.of(rule1, rule2); | |||
assertThat(esSetup.exists("rules", "rule", "3")).isTrue(); | |||
when(ruleDao.selectNonManual()).thenReturn(rules); | |||
when(ruleDao.selectNonManual(any(SqlSession.class))).thenReturn(rules); | |||
final Multimap<Integer, RuleParamDto> params = ArrayListMultimap.create(); | |||
final Multimap<Integer, RuleTagDto> tags = ArrayListMultimap.create(); | |||
registry.bulkRegisterRules(rules, params, tags); |
@@ -0,0 +1,82 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.rule; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleDefinitions; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class RuleRepositoriesTest { | |||
@Test | |||
public void should_register_repositories() { | |||
RuleDefinitions.Context context = new RuleDefinitions.Context(); | |||
new SquidDefinitions().define(context); | |||
new FindbugsDefinitions().define(context); | |||
RuleRepositories repositories = new RuleRepositories(); | |||
repositories.register(context); | |||
RuleRepositories.Repository findbugs = repositories.repository("findbugs"); | |||
assertThat(findbugs).isNotNull(); | |||
assertThat(findbugs.key()).isEqualTo("findbugs"); | |||
assertThat(findbugs.name()).isEqualTo("Findbugs"); | |||
assertThat(findbugs.language()).isEqualTo("java"); | |||
assertThat(findbugs.getKey()).isEqualTo("findbugs"); | |||
assertThat(findbugs.getName()).isEqualTo("Findbugs"); | |||
assertThat(findbugs.getLanguage()).isEqualTo("java"); | |||
RuleRepositories.Repository squid = repositories.repository("squid"); | |||
assertThat(squid).isNotNull(); | |||
assertThat(squid.key()).isEqualTo("squid"); | |||
assertThat(squid.name()).isEqualTo("Squid"); | |||
assertThat(squid.language()).isEqualTo("java"); | |||
assertThat(repositories.repositories()).containsOnly(findbugs, squid); | |||
assertThat(repositories.repositoriesForLang("java")).containsOnly(findbugs, squid); | |||
// test equals() and hashCode() | |||
assertThat(findbugs).isEqualTo(findbugs).isNotEqualTo(squid).isNotEqualTo("findbugs").isNotEqualTo(null); | |||
} | |||
static class FindbugsDefinitions implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("findbugs", "java"); | |||
repo.setName("Findbugs"); | |||
repo.newRule("ABC") | |||
.setName("ABC") | |||
.setHtmlDescription("Description of ABC"); | |||
repo.done(); | |||
} | |||
} | |||
static class SquidDefinitions implements RuleDefinitions { | |||
@Override | |||
public void define(Context context) { | |||
NewRepository repo = context.newRepository("squid", "java"); | |||
repo.setName("Squid"); | |||
repo.newRule("DEF") | |||
.setName("DEF") | |||
.setHtmlDescription("Description of DEF"); | |||
repo.done(); | |||
} | |||
} | |||
} |
@@ -1,60 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.rules; | |||
import org.hamcrest.Matchers; | |||
import org.junit.Test; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RuleRepository; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import static org.hamcrest.CoreMatchers.not; | |||
import static org.hamcrest.Matchers.is; | |||
import static org.junit.Assert.assertThat; | |||
public class RulesConsoleTest { | |||
@Test | |||
public void shouldIgnoreRepositoryExtensions() throws Exception { | |||
RuleRepository[] repositories = new RuleRepository[]{ | |||
new FakeRepository("findbugs", "java"), | |||
new FakeRepository("findbugs", "java"), // for example fb-contrib | |||
}; | |||
RulesConsole console = new RulesConsole(repositories); | |||
assertThat(console.getRepository("findbugs"), not(Matchers.nullValue())); | |||
assertThat(console.getRepositoriesByLanguage("java").size(), is(1)); | |||
} | |||
private static class FakeRepository extends RuleRepository { | |||
private FakeRepository(String key, String language) { | |||
super(key, language); | |||
} | |||
@Override | |||
public List<Rule> createRules() { | |||
return Collections.emptyList(); | |||
} | |||
} | |||
} | |||
@@ -1,367 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube 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. | |||
* | |||
* SonarQube 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.startup; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RulePriority; | |||
import org.sonar.api.rules.RuleRepository; | |||
import org.sonar.api.utils.SonarException; | |||
import org.sonar.core.i18n.RuleI18nManager; | |||
import org.sonar.core.persistence.AbstractDaoTestCase; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.qualityprofile.db.ActiveRuleDao; | |||
import org.sonar.core.rule.RuleDao; | |||
import org.sonar.server.configuration.ProfilesManager; | |||
import org.sonar.server.rule.RuleRegistry; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Matchers.anyInt; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.mockito.Mockito.*; | |||
public class RegisterRulesTest extends AbstractDaoTestCase { | |||
private static final String[] EXCLUDED_COLUMN_NAMES = { | |||
"created_at", "updated_at", "note_data", "note_user_login", "note_created_at", "note_updated_at"}; | |||
RegisterRules task; | |||
ProfilesManager profilesManager; | |||
RuleRegistry ruleRegistry; | |||
RuleI18nManager ruleI18nManager; | |||
MyBatis myBatis; | |||
RuleDao ruleDao; | |||
ActiveRuleDao activeRuleDao; | |||
@Before | |||
public void init() { | |||
profilesManager = mock(ProfilesManager.class); | |||
ruleRegistry = mock(RuleRegistry.class); | |||
ruleI18nManager = mock(RuleI18nManager.class); | |||
myBatis = getMyBatis(); | |||
ruleDao = new RuleDao(myBatis); | |||
activeRuleDao = new ActiveRuleDao(myBatis); | |||
task = new RegisterRules(new RuleRepository[]{new FakeRepository()}, ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
} | |||
@Test | |||
public void should_save_new_repositories() { | |||
setupData("shared"); | |||
task.start(); | |||
checkTables("should_save_new_repositories", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters", "rule_tags"); | |||
} | |||
@Test | |||
public void should_update_template_rule() { | |||
setupData("should_update_template_rule_language"); | |||
task.start(); | |||
checkTables("should_update_template_rule_language", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_notify_for_removed_rules() { | |||
setupData("shared"); | |||
task.start(); | |||
verify(profilesManager).removeActivatedRules(anyInt()); | |||
} | |||
@Test | |||
public void should_reactivate_disabled_rules() { | |||
setupData("reactivateDisabledRules"); | |||
task.start(); | |||
checkTables("reactivateDisabledRules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
assertThat(ruleDao.selectById(1).getUpdatedAt()).isNotNull(); | |||
} | |||
@Test | |||
public void should_not_reactivate_disabled_template_rules() { | |||
setupData("should_reactivate_disabled_template_rules"); | |||
task.start(); | |||
checkTables("should_reactivate_disabled_template_rules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_not_update_already_disabled_rules() { | |||
setupData("notUpdateAlreadyDisabledRule"); | |||
task.start(); | |||
checkTables("should_save_new_repositories", EXCLUDED_COLUMN_NAMES, "rules"); | |||
assertThat(ruleDao.selectById(1).getUpdatedAt()).isNull(); | |||
} | |||
@Test | |||
public void should_disable_deprecated_active_rules() { | |||
setupData("disableDeprecatedActiveRules"); | |||
task.start(); | |||
checkTables("disableDeprecatedActiveRules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_disable_deprecated_active_rule_parameters() { | |||
setupData("disableDeprecatedActiveRuleParameters"); | |||
task.start(); | |||
checkTables("disableDeprecatedActiveRuleParameters", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters", "active_rules", "active_rule_parameters"); | |||
} | |||
@Test | |||
public void should_disable_deprecated_rules() { | |||
setupData("disableDeprecatedRules"); | |||
task.start(); | |||
checkTables("disableDeprecatedRules", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters"); | |||
} | |||
@Test | |||
public void should_update_rule_fields() { | |||
setupData("updadeRuleFields"); | |||
task.start(); | |||
checkTables("updadeRuleFields", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters", "rule_tags"); | |||
} | |||
@Test | |||
public void should_store_bundle_name_and_description_in_database() { | |||
setupData("updadeRuleFields"); | |||
String i18nName = "The One"; | |||
String i18nDescription = "The Description of One"; | |||
when(ruleI18nManager.getName("fake", "rule1")).thenReturn(i18nName); | |||
when(ruleI18nManager.getDescription("fake", "rule1")).thenReturn(i18nDescription); | |||
task.start(); | |||
checkTables("should_store_bundle_name_and_description_in_database", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_update_rule_parameters() { | |||
setupData("updateRuleParameters"); | |||
task.start(); | |||
checkTables("updateRuleParameters", EXCLUDED_COLUMN_NAMES, "rules", "rules_parameters"); | |||
} | |||
@Test | |||
public void should_not_disable_template_rules_if_parent_is_enabled() { | |||
setupData("doNotDisableUserRulesIfParentIsEnabled"); | |||
task.start(); | |||
checkTables("doNotDisableUserRulesIfParentIsEnabled", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_disable_template_rules_if_parent_is_disabled() { | |||
setupData("disableUserRulesIfParentIsDisabled"); | |||
task.start(); | |||
checkTables("disableUserRulesIfParentIsDisabled", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void should_not_disable_manual_rules() { | |||
// the hardcoded repository "manual" is used for manual violations | |||
setupData("shouldNotDisableManualRules"); | |||
task.start(); | |||
checkTables("shouldNotDisableManualRules", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
public void volume_testing() { | |||
task = new RegisterRules(new RuleRepository[]{new VolumeRepository()}, ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("shared"); | |||
task.start(); | |||
// There is already one rule in DB | |||
assertThat(ruleDao.selectAll()).hasSize(VolumeRepository.SIZE + 1); | |||
} | |||
// SONAR-3305 | |||
@Test | |||
public void should_fail_with_rule_without_name() throws Exception { | |||
task = new RegisterRules(new RuleRepository[]{new RuleWithoutNameRepository()}, ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("shared"); | |||
// the rule has no name, it should fail | |||
try { | |||
task.start(); | |||
fail("Rule must have a name"); | |||
} catch (SonarException e) { | |||
assertThat(e.getMessage()).contains("must have a name"); | |||
} | |||
// now it is ok, the rule has a name in the English bundle | |||
when(ruleI18nManager.getName(anyString(), anyString())).thenReturn("Name"); | |||
when(ruleI18nManager.getDescription(anyString(), anyString())).thenReturn("Description"); | |||
task.start(); | |||
} | |||
// SONAR-3769 | |||
@Test | |||
public void should_fail_with_rule_with_blank_name() throws Exception { | |||
task = new RegisterRules(new RuleRepository[]{new RuleWithoutNameRepository()}, ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("shared"); | |||
// the rule has no name, it should fail | |||
try { | |||
task.start(); | |||
fail("Rule must have a name"); | |||
} catch (SonarException e) { | |||
assertThat(e.getMessage()).contains("must have a name"); | |||
} | |||
} | |||
// SONAR-3305 | |||
@Test | |||
public void should_fail_with_rule_without_description() throws Exception { | |||
when(ruleI18nManager.getName(anyString(), anyString())).thenReturn("Name"); | |||
task = new RegisterRules(new RuleRepository[]{new RuleWithoutDescriptionRepository()}, ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("shared"); | |||
// the rule has no name, it should fail | |||
try { | |||
task.start(); | |||
fail("Rule must have a description"); | |||
} catch (SonarException e) { | |||
assertThat(e.getMessage()).contains("must have a description"); | |||
} | |||
// now it is ok, the rule has a name & a description in the English bundle | |||
when(ruleI18nManager.getName(anyString(), anyString())).thenReturn("Name"); | |||
when(ruleI18nManager.getDescription(anyString(), anyString())).thenReturn("Description"); | |||
task.start(); | |||
} | |||
// http://jira.codehaus.org/browse/SONAR-3722 | |||
@Test | |||
public void should_fail_with_rule_without_name_in_bundle() throws Exception { | |||
task = new RegisterRules(new RuleRepository[]{new RuleWithoutDescriptionRepository()}, ruleI18nManager, profilesManager, ruleRegistry, myBatis, ruleDao, activeRuleDao); | |||
setupData("shared"); | |||
// the rule has no name, it should fail | |||
try { | |||
task.start(); | |||
fail("Rule must have a description"); | |||
} catch (SonarException e) { | |||
assertThat(e.getMessage()).contains("No description found for the rule 'Rule 1' (repository: rule-without-description-repo) " + | |||
"because the entry 'rule.rule-without-description-repo.rule1.name' is missing from the bundle."); | |||
} | |||
} | |||
} | |||
class FakeRepository extends RuleRepository { | |||
public FakeRepository() { | |||
super("fake", "java"); | |||
} | |||
@Override | |||
public List<Rule> createRules() { | |||
Rule rule1 = Rule.create("fake", "rule1", "One"); | |||
rule1.setDescription("Description of One"); | |||
rule1.setSeverity(RulePriority.BLOCKER); | |||
rule1.setConfigKey("config1"); | |||
rule1.createParameter("param1").setDescription("parameter one").setDefaultValue("default value one"); | |||
rule1.createParameter("param2").setDescription("parameter two").setDefaultValue("default value two"); | |||
rule1.setTags("tag1", "tag3", "tag5"); | |||
Rule rule2 = Rule.create("fake", "rule2", "Two"); | |||
rule2.setDescription("Description of Two"); | |||
rule2.setSeverity(RulePriority.INFO); | |||
rule2.setStatus(Rule.STATUS_DEPRECATED); | |||
return Arrays.asList(rule1, rule2); | |||
} | |||
} | |||
class RuleWithoutNameRepository extends RuleRepository { | |||
public RuleWithoutNameRepository() { | |||
super("rule-without-name-repo", "java"); | |||
} | |||
@Override | |||
public List<Rule> createRules() { | |||
// Rules must not have empty name | |||
Rule rule1 = Rule.create("fake", "rule1", null); | |||
return Arrays.asList(rule1); | |||
} | |||
} | |||
class RuleWithoutDescriptionRepository extends RuleRepository { | |||
public RuleWithoutDescriptionRepository() { | |||
super("rule-without-description-repo", "java"); | |||
} | |||
@Override | |||
public List<Rule> createRules() { | |||
// Rules must not have empty description | |||
Rule rule1 = Rule.create("fake", "rule1", "Rule 1"); | |||
return Arrays.asList(rule1); | |||
} | |||
} | |||
class VolumeRepository extends RuleRepository { | |||
static final int SIZE = 500; | |||
public VolumeRepository() { | |||
super("volume", "java"); | |||
} | |||
@Override | |||
public List<Rule> createRules() { | |||
List<Rule> rules = new ArrayList<Rule>(); | |||
for (int i = 0; i < SIZE; i++) { | |||
Rule rule = Rule.create("volume", "rule" + i, "name of " + i); | |||
rule.setDescription("description of " + i); | |||
rule.setSeverity(RulePriority.BLOCKER); | |||
for (int j = 0; j < 20; j++) { | |||
rule.createParameter("param" + j); | |||
} | |||
rules.add(rule); | |||
} | |||
return rules; | |||
} | |||
} | |||
class RuleWithUnkownStatusRepository extends RuleRepository { | |||
public RuleWithUnkownStatusRepository() { | |||
super("rule-with-unknwon-status-repo", "java"); | |||
} | |||
@Override | |||
public List<Rule> createRules() { | |||
Rule rule1 = Rule.create("fake", "rule1", "rule1").setDescription("Description").setStatus("UNKNOWN"); | |||
return Arrays.asList(rule1); | |||
} | |||
} |
@@ -15,7 +15,7 @@ | |||
<rules id="5" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="6" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="6" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -6,7 +6,7 @@ | |||
<rules id="2" plugin_rule_key="user_rule" plugin_name="fake" plugin_config_key="[null]" name="User rule" description="[null]" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="1" language="java"/> | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -0,0 +1 @@ | |||
<dataset></dataset> |
@@ -9,7 +9,7 @@ | |||
<rules id="3" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="4" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="4" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -3,7 +3,7 @@ | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules_parameters id="1" rule_id="1" default_value="default value one" description="parameter one" name="param1" param_type="STRING"/> |
@@ -9,7 +9,7 @@ | |||
<rules id="3" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="4" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="4" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -14,7 +14,7 @@ | |||
<rules_parameters id="2" rule_id="3" default_value="default value one" description="parameter one" name="param1" param_type="STRING"/> | |||
<rules_parameters id="3" rule_id="3" default_value="default value two" description="parameter two" name="param2" param_type="STRING"/> | |||
<rules id="4" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="4" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -0,0 +1,9 @@ | |||
<dataset> | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="findbugs" plugin_config_key="[null]" name="Rule One" description="Description of Rule One" | |||
status="READY" priority="2" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="findbugs" plugin_config_key="[null]" name="Rule Two" description="Description of Rule Two" | |||
status="READY" priority="2" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -11,7 +11,7 @@ | |||
<rule_tags id="2" rule_id="2" tag="tag3" tag_type="SYSTEM"/> | |||
<rule_tags id="3" rule_id="2" tag="tag5" tag_type="SYSTEM"/> | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -6,7 +6,7 @@ | |||
<rules id="2" plugin_rule_key="user_rule" plugin_name="fake" plugin_config_key="[null]" name="User rule" description="[null]" | |||
status="REMOVED" priority="4" cardinality="SINGLE" parent_id="1" language="java"/> | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -1,9 +1,9 @@ | |||
<dataset> | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="REMOVED" priority="4" cardinality="SINGLE" parent_id="[null]"/> | |||
status="REMOVED" priority="4" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="2" plugin_rule_key="user_rule" plugin_name="fake" plugin_config_key="[null]" name="User rule" description="[null]" | |||
status="REMOVED" priority="4" cardinality="SINGLE" parent_id="1"/> | |||
status="REMOVED" priority="4" cardinality="SINGLE" parent_id="1" language="java"/> | |||
</dataset> |
@@ -3,7 +3,7 @@ | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -1,6 +1,6 @@ | |||
<dataset> | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="[null]" name="Old name" description="Old description" | |||
status="REMOVED" priority="4" cardinality="SINGLE" parent_id="[null]"/> | |||
</dataset> |
@@ -5,7 +5,7 @@ | |||
<rules_parameters id="1" rule_id="1" default_value="default value one" description="parameter one" name="param1" param_type="STRING"/> | |||
<rules_parameters id="3" rule_id="1" default_value="default value two" description="parameter two" name="param2" param_type="STRING"/> | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |
@@ -7,11 +7,11 @@ | |||
<rules id="2" plugin_rule_key="template_rule1" plugin_name="fake" plugin_config_key="[null]" name="User rule" description="[null]" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="1" language="java"/> | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
<!-- Template of old rule 3 :/ --> | |||
<rules id="4" plugin_rule_key="template_rule2" plugin_name="fake" plugin_config_key="[null]" name="User rule" description="[null]" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="3" language="java"/> | |||
status="DEPRECATED" priority="4" cardinality="SINGLE" parent_id="3" language="java"/> | |||
</dataset> |
@@ -5,7 +5,7 @@ | |||
<!-- Instance of rule 1 --> | |||
<rules id="2" plugin_rule_key="template_rule1" plugin_name="fake" plugin_config_key="[null]" name="User rule" description="[null]" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="1"/> | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="1" language="[null]"/> | |||
<rules id="3" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="old_config_key2" name="old name2" description="old description2" | |||
status="DEPRECATED" priority="1" cardinality="MULTIPLE" parent_id="[null]" /> |
@@ -11,7 +11,7 @@ | |||
<rule_tags id="5" rule_id="1" tag="tag3" tag_type="SYSTEM"/> | |||
<rule_tags id="6" rule_id="1" tag="tag5" tag_type="SYSTEM"/> | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="rule2" name="Two" description="Description of Two" | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java"/> | |||
</dataset> |