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    }