001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.server.startup;
021
022 import com.google.common.collect.Lists;
023 import com.google.common.collect.Maps;
024 import org.apache.commons.lang.StringUtils;
025 import org.sonar.api.database.DatabaseSession;
026 import org.sonar.api.rules.ActiveRuleParam;
027 import org.sonar.api.rules.Rule;
028 import org.sonar.api.rules.RuleParam;
029 import org.sonar.api.rules.RuleRepository;
030 import org.sonar.api.utils.Logs;
031 import org.sonar.api.utils.SonarException;
032 import org.sonar.api.utils.TimeProfiler;
033 import org.sonar.core.i18n.RuleI18nManager;
034 import org.sonar.jpa.session.DatabaseSessionFactory;
035
036 import java.util.*;
037
038 public final class RegisterRules {
039
040 private DatabaseSessionFactory sessionFactory;
041 private List<RuleRepository> repositories = Lists.newArrayList();
042 private RuleI18nManager ruleI18nManager;
043
044 public RegisterRules(DatabaseSessionFactory sessionFactory, RuleRepository[] repos, RuleI18nManager ruleI18nManager) {
045 this.sessionFactory = sessionFactory;
046 this.repositories.addAll(Arrays.asList(repos));
047 this.ruleI18nManager = ruleI18nManager;
048 }
049
050 public RegisterRules(DatabaseSessionFactory sessionFactory, RuleI18nManager ruleI18nManager) {
051 this(sessionFactory, new RuleRepository[0], ruleI18nManager);
052 }
053
054 public void start() {
055 TimeProfiler profiler = new TimeProfiler();
056
057 DatabaseSession session = sessionFactory.getSession();
058 disableAllRules(session);
059 for (RuleRepository repository : repositories) {
060 profiler.start("Register rules [" + repository.getKey() + "/" + StringUtils.defaultString(repository.getLanguage(), "-") + "]");
061 registerRepository(repository, session);
062 profiler.stop();
063 }
064
065 profiler.start("Disable deprecated user rules");
066 disableDeprecatedUserRules(session);
067 profiler.stop();
068
069 session.commit();
070 }
071
072 private void disableDeprecatedUserRules(DatabaseSession session) {
073 List<Integer> deprecatedUserRuleIds = Lists.newLinkedList();
074 deprecatedUserRuleIds.addAll(session.createQuery(
075 "SELECT r.id FROM " + Rule.class.getSimpleName() +
076 " r WHERE r.parent IS NOT NULL AND NOT EXISTS(FROM " + Rule.class.getSimpleName() + " p WHERE r.parent=p)").getResultList());
077
078 deprecatedUserRuleIds.addAll(session.createQuery(
079 "SELECT r.id FROM " + Rule.class.getSimpleName() +
080 " r WHERE r.parent IS NOT NULL AND EXISTS(FROM " + Rule.class.getSimpleName() + " p WHERE r.parent=p and p.enabled=false)").getResultList());
081
082 for (Integer deprecatedUserRuleId : deprecatedUserRuleIds) {
083 Rule rule = session.getSingleResult(Rule.class, "id", deprecatedUserRuleId);
084 rule.setEnabled(false);
085 session.saveWithoutFlush(rule);
086 }
087
088 }
089
090 private void disableAllRules(DatabaseSession session) {
091 // the hardcoded repository "manual" is used for manual violations
092 session.createQuery("UPDATE " + Rule.class.getSimpleName() + " SET enabled=false WHERE parent IS NULL AND pluginName<>'manual'").executeUpdate();
093 }
094
095 private void registerRepository(RuleRepository repository, DatabaseSession session) {
096 Map<String, Rule> rulesByKey = Maps.newHashMap();
097 for (Rule rule : repository.createRules()) {
098 validateRule(rule, repository.getKey());
099 rule.setRepositoryKey(repository.getKey());
100 rulesByKey.put(rule.getKey(), rule);
101 }
102 Logs.INFO.info(rulesByKey.size() + " rules");
103
104 List<Rule> persistedRules = session.getResults(Rule.class, "pluginName", repository.getKey());
105 for (Rule persistedRule : persistedRules) {
106 Rule rule = rulesByKey.get(persistedRule.getKey());
107 if (rule != null) {
108 updateRule(persistedRule, rule, session);
109 rulesByKey.remove(rule.getKey());
110 }
111 }
112
113 saveNewRules(rulesByKey.values(), session);
114 }
115
116 private void validateRule(Rule rule, String repositoryKey) {
117 if (rule.getName() == null && ruleI18nManager.getName(repositoryKey, rule.getKey(), Locale.ENGLISH) == null) {
118 throw new SonarException("The following rule (repository: " + repositoryKey + ") must have a name: " + rule);
119 }
120 if (rule.getDescription() == null && ruleI18nManager.getDescription(repositoryKey, rule.getKey(), Locale.ENGLISH) == null) {
121 throw new SonarException("The following rule (repository: " + repositoryKey + ") must have a description: " + rule);
122 }
123 }
124
125 private void updateRule(Rule persistedRule, Rule rule, DatabaseSession session) {
126 persistedRule.setName(rule.getName());
127 persistedRule.setConfigKey(rule.getConfigKey());
128 persistedRule.setDescription(rule.getDescription());
129 persistedRule.setSeverity(rule.getSeverity());
130 persistedRule.setEnabled(true);
131 persistedRule.setCardinality(rule.getCardinality());
132
133 // delete deprecated params
134 deleteDeprecatedParameters(persistedRule, rule, session);
135
136 // add new params and update existing params
137 updateParameters(persistedRule, rule);
138
139 session.saveWithoutFlush(persistedRule);
140 }
141
142 private void updateParameters(Rule persistedRule, Rule rule) {
143 if (rule.getParams() != null) {
144 for (RuleParam param : rule.getParams()) {
145 RuleParam persistedParam = persistedRule.getParam(param.getKey());
146 if (persistedParam == null) {
147 persistedParam = persistedRule.createParameter(param.getKey());
148 }
149 persistedParam.setDescription(param.getDescription());
150 persistedParam.setType(param.getType());
151 persistedParam.setDefaultValue(param.getDefaultValue());
152 }
153 }
154 }
155
156 private void deleteDeprecatedParameters(Rule persistedRule, Rule rule, DatabaseSession session) {
157 if (persistedRule.getParams() != null && persistedRule.getParams().size() > 0) {
158 for (Iterator<RuleParam> it = persistedRule.getParams().iterator(); it.hasNext();) {
159 RuleParam persistedParam = it.next();
160 if (rule.getParam(persistedParam.getKey()) == null) {
161 it.remove();
162 session
163 .createQuery("delete from " + ActiveRuleParam.class.getSimpleName() + " where ruleParam=:param")
164 .setParameter("param", persistedParam)
165 .executeUpdate();
166 }
167 }
168 }
169 }
170
171 private void saveNewRules(Collection<Rule> rules, DatabaseSession session) {
172 for (Rule rule : rules) {
173 rule.setEnabled(true);
174 session.saveWithoutFlush(rule);
175 }
176 }
177 }