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 */
020package org.sonar.server.startup;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import org.apache.commons.lang.StringUtils;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032import org.sonar.api.database.DatabaseSession;
033import org.sonar.api.profiles.ProfileDefinition;
034import org.sonar.api.profiles.RulesProfile;
035import org.sonar.api.rules.ActiveRule;
036import org.sonar.api.rules.Rule;
037import org.sonar.api.rules.RuleFinder;
038import org.sonar.api.rules.RuleParam;
039import org.sonar.api.rules.RuleQuery;
040import org.sonar.api.utils.SonarException;
041import org.sonar.api.utils.TimeProfiler;
042import org.sonar.api.utils.ValidationMessages;
043import org.sonar.jpa.session.DatabaseSessionFactory;
044
045import com.google.common.collect.Lists;
046import com.google.common.collect.Maps;
047import com.google.common.collect.Sets;
048
049public final class RegisterProvidedProfiles {
050
051  private static final Logger LOGGER = LoggerFactory.getLogger(RegisterProvidedProfiles.class);
052
053  private DatabaseSessionFactory sessionFactory;
054  private List<ProfileDefinition> definitions = Lists.newArrayList();
055  private RuleFinder ruleFinder;
056
057  public RegisterProvidedProfiles(RuleFinder ruleFinder, DatabaseSessionFactory sessionFactory,// NOSONAR the parameter registerRulesBefore is unused must be declared for execution order of tasks
058                                  RegisterRules registerRulesBefore,
059                                  ProfileDefinition[] definitions) {
060    this.ruleFinder = ruleFinder;
061    this.sessionFactory = sessionFactory;
062    this.definitions.addAll(Arrays.asList(definitions));
063  }
064
065  public RegisterProvidedProfiles(RuleFinder ruleFinder, DatabaseSessionFactory sessionFactory,// NOSONAR the parameter registerRulesBefore is unused must be declared for execution order of tasks
066                                  RegisterRules registerRulesBefore) {
067    this.ruleFinder = ruleFinder;
068    this.sessionFactory = sessionFactory;
069  }
070
071  public void start() {
072    TimeProfiler profiler = new TimeProfiler().start("Load provided profiles");
073
074    List<RulesProfile> profiles = createProfiles();
075    DatabaseSession session = sessionFactory.getSession();
076    cleanProvidedProfiles(profiles, session);
077    saveProvidedProfiles(profiles, session);
078    session.commit();
079    profiler.stop();
080  }
081
082  private List<RulesProfile> createProfiles() {
083    List<RulesProfile> result = Lists.newArrayList();
084    Map<String, RulesProfile> defaultProfilesByLanguage = Maps.newHashMap();
085    for (ProfileDefinition definition : definitions) {
086      ValidationMessages validation = ValidationMessages.create();
087      RulesProfile profile = definition.createProfile(validation);
088      validation.log(LOGGER);
089      if (profile != null && !validation.hasErrors()) {
090        result.add(profile);
091        checkIfNoMoreThanOneDefaultProfile(defaultProfilesByLanguage, profile);
092      }
093    }
094    return result;
095  }
096
097  private void checkIfNoMoreThanOneDefaultProfile(Map<String, RulesProfile> defaultProfilesByLanguage, RulesProfile profile) {
098    if (profile.getDefaultProfile()) {
099      RulesProfile defaultProfileForLanguage = defaultProfilesByLanguage.get(profile.getLanguage());
100      if (defaultProfileForLanguage == null) {
101        defaultProfilesByLanguage.put(profile.getLanguage(), profile);
102      } else {
103        throw new SonarException("Language " + profile.getLanguage() + " can't have 2 default provided profiles: "
104          + profile.getName() + " and "
105          + defaultProfileForLanguage.getName());
106      }
107    }
108  }
109
110  private void cleanProvidedProfiles(List<RulesProfile> profiles, DatabaseSession session) {
111    TimeProfiler profiler = new TimeProfiler().start("Clean provided profiles");
112    List<RulesProfile> existingProfiles = session.getResults(RulesProfile.class, "provided", true);
113    for (RulesProfile existingProfile : existingProfiles) {
114      boolean isDeprecated = true;
115      for (RulesProfile profile : profiles) {
116        if (StringUtils.equals(existingProfile.getName(), profile.getName()) && StringUtils.equals(existingProfile.getLanguage(), profile.getLanguage())) {
117          isDeprecated = false;
118          break;
119        }
120      }
121      if (isDeprecated) {
122        session.removeWithoutFlush(existingProfile);
123      } else {
124        for (ActiveRule activeRule : existingProfile.getActiveRules()) {
125          session.removeWithoutFlush(activeRule);
126        }
127        existingProfile.setActiveRules(new ArrayList<ActiveRule>());
128        session.saveWithoutFlush(existingProfile);
129      }
130    }
131    profiler.stop();
132  }
133
134  private void saveProvidedProfiles(List<RulesProfile> profiles, DatabaseSession session) {
135    Collection<String> languagesWithDefaultProfile = findLanguagesWithDefaultProfile(session);
136    for (RulesProfile profile : profiles) {
137      TimeProfiler profiler = new TimeProfiler().start("Save profile " + profile);
138      RulesProfile persistedProfile = findOrCreate(profile, session, languagesWithDefaultProfile.contains(profile.getLanguage()));
139
140      for (ActiveRule activeRule : profile.getActiveRules()) {
141        Rule rule = getPersistedRule(activeRule);
142        ActiveRule persistedRule = persistedProfile.activateRule(rule, activeRule.getSeverity());
143        for (RuleParam param : rule.getParams()) {
144          String value = StringUtils.defaultString(activeRule.getParameter(param.getKey()), param.getDefaultValue());
145          if (value != null) {
146            persistedRule.setParameter(param.getKey(), value);
147          }
148        }
149      }
150
151      session.saveWithoutFlush(persistedProfile);
152      profiler.stop();
153    }
154  }
155
156  private Collection<String> findLanguagesWithDefaultProfile(DatabaseSession session) {
157    Set<String> languagesWithDefaultProfile = Sets.newHashSet();
158    List<RulesProfile> defaultProfiles = session.getResults(RulesProfile.class, "defaultProfile", true);
159    for (RulesProfile defaultProfile : defaultProfiles) {
160      languagesWithDefaultProfile.add(defaultProfile.getLanguage());
161    }
162    return languagesWithDefaultProfile;
163  }
164
165  private Rule getPersistedRule(ActiveRule activeRule) {
166    Rule rule = activeRule.getRule();
167    if (rule != null && rule.getId() == null) {
168      if (rule.getKey() != null) {
169        rule = ruleFinder.findByKey(rule.getRepositoryKey(), rule.getKey());
170
171      } else if (rule.getConfigKey() != null) {
172        rule = ruleFinder.find(RuleQuery.create().withRepositoryKey(rule.getRepositoryKey()).withConfigKey(rule.getConfigKey()));
173      }
174    }
175    return rule;
176  }
177
178  private RulesProfile findOrCreate(RulesProfile profile, DatabaseSession session, boolean defaultProfileAlreadyExist) {
179    RulesProfile persistedProfile = session.getSingleResult(RulesProfile.class, "name", profile.getName(), "language", profile.getLanguage());
180    if (persistedProfile == null) {
181      persistedProfile = RulesProfile.create(profile.getName(), profile.getLanguage());
182      persistedProfile.setProvided(true);
183      if (!defaultProfileAlreadyExist) {
184        persistedProfile.setDefaultProfile(profile.getDefaultProfile());
185      }
186    }
187    return persistedProfile;
188  }
189
190}