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 java.util.ArrayList;
023 import java.util.Arrays;
024 import java.util.Collection;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Set;
028
029 import org.apache.commons.lang.StringUtils;
030 import org.slf4j.Logger;
031 import org.slf4j.LoggerFactory;
032 import org.sonar.api.database.DatabaseSession;
033 import org.sonar.api.profiles.ProfileDefinition;
034 import org.sonar.api.profiles.RulesProfile;
035 import org.sonar.api.rules.ActiveRule;
036 import org.sonar.api.rules.Rule;
037 import org.sonar.api.rules.RuleFinder;
038 import org.sonar.api.rules.RuleParam;
039 import org.sonar.api.rules.RuleQuery;
040 import org.sonar.api.utils.SonarException;
041 import org.sonar.api.utils.TimeProfiler;
042 import org.sonar.api.utils.ValidationMessages;
043 import org.sonar.jpa.session.DatabaseSessionFactory;
044
045 import com.google.common.collect.Lists;
046 import com.google.common.collect.Maps;
047 import com.google.common.collect.Sets;
048
049 public 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 }