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.configuration;
021
022 import org.apache.commons.lang.ObjectUtils;
023 import org.codehaus.plexus.util.StringUtils;
024 import org.sonar.api.database.DatabaseSession;
025 import org.sonar.api.database.model.ResourceModel;
026 import org.sonar.api.profiles.RulesProfile;
027 import org.sonar.api.rules.*;
028 import org.sonar.api.utils.ValidationMessages;
029 import org.sonar.jpa.dao.BaseDao;
030 import org.sonar.jpa.dao.RulesDao;
031
032 import java.util.List;
033
034 public class ProfilesManager extends BaseDao {
035
036 private RulesDao rulesDao;
037
038 public ProfilesManager(DatabaseSession session, RulesDao rulesDao) {
039 super(session);
040 this.rulesDao = rulesDao;
041 }
042
043 public void renameProfile(int profileId, String newProfileName) {
044 RulesProfile profile = getSession().getSingleResult(RulesProfile.class, "id", profileId);
045 if (profile != null && !profile.getProvided()) {
046 String hql = "UPDATE " + RulesProfile.class.getSimpleName() + " o SET o.parentName=:newName WHERE o.parentName=:oldName";
047 getSession().getEntityManager().createQuery(hql)
048 .setParameter("oldName", profile.getName())
049 .setParameter("newName", newProfileName)
050 .executeUpdate();
051 profile.setName(newProfileName);
052 getSession().save(profile);
053 getSession().commit();
054 }
055 }
056
057 public void copyProfile(int profileId, String newProfileName) {
058 RulesProfile profile = getSession().getSingleResult(RulesProfile.class, "id", profileId);
059 RulesProfile toImport = (RulesProfile) profile.clone();
060 toImport.setName(newProfileName);
061 toImport.setDefaultProfile(false);
062 toImport.setProvided(false);
063 ProfilesBackup pb = new ProfilesBackup(getSession());
064 pb.importProfile(rulesDao, toImport);
065 getSession().commit();
066 }
067
068 public void deleteProfile(int profileId) {
069 RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId);
070 if (profile != null && !profile.getProvided() && getChildren(profile).isEmpty()) {
071 // Remove history of rule changes
072 String hqlDeleteRc = "DELETE " + ActiveRuleChange.class.getSimpleName() + " rc WHERE rc.rulesProfile=:rulesProfile";
073 getSession().createQuery(hqlDeleteRc).setParameter("rulesProfile", profile).executeUpdate();
074
075 String hql = "UPDATE " + ResourceModel.class.getSimpleName() + " o SET o.rulesProfile=null WHERE o.rulesProfile=:rulesProfile";
076 getSession().createQuery(hql).setParameter("rulesProfile", profile).executeUpdate();
077 getSession().remove(profile);
078 getSession().commit();
079 }
080 }
081
082 public void deleteAllProfiles() {
083 // Remove history of rule changes
084 String hqlDeleteRc = "DELETE " + ActiveRuleChange.class.getSimpleName() + " rc";
085 getSession().createQuery(hqlDeleteRc).executeUpdate();
086
087 String hql = "UPDATE " + ResourceModel.class.getSimpleName() + " o SET o.rulesProfile = null WHERE o.rulesProfile IS NOT NULL";
088 getSession().createQuery(hql).executeUpdate();
089 List profiles = getSession().createQuery("FROM " + RulesProfile.class.getSimpleName()).getResultList();
090 for (Object profile : profiles) {
091 getSession().removeWithoutFlush(profile);
092 }
093 getSession().commit();
094 }
095
096 // Managing inheritance of profiles
097
098 public ValidationMessages changeParentProfile(Integer profileId, String parentName, String userName) {
099 ValidationMessages messages = ValidationMessages.create();
100 RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId);
101 if (profile != null && !profile.getProvided()) {
102 RulesProfile oldParent = getParentProfile(profile);
103 RulesProfile newParent = getProfile(profile.getLanguage(), parentName);
104 if (isCycle(profile, newParent)) {
105 messages.addWarningText("Please do not select a child profile as parent.");
106 return messages;
107 }
108 // Deactivate all inherited rules
109 if (oldParent != null) {
110 for (ActiveRule activeRule : oldParent.getActiveRules()) {
111 deactivate(profile, activeRule.getRule(), userName);
112 }
113 }
114 // Activate all inherited rules
115 if (newParent != null) {
116 for (ActiveRule activeRule : newParent.getActiveRules()) {
117 activateOrChange(profile, activeRule, userName);
118 }
119 }
120 profile.setParentName(newParent == null ? null : newParent.getName());
121 getSession().saveWithoutFlush(profile);
122 getSession().commit();
123 }
124 return messages;
125 }
126
127 /**
128 * Rule was activated
129 */
130 public void activated(int profileId, int activeRuleId, String userName) {
131 ActiveRule activeRule = getSession().getEntity(ActiveRule.class, activeRuleId);
132 RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId);
133 ruleEnabled(profile, activeRule, userName);
134 // Notify child profiles
135 activatedOrChanged(profileId, activeRuleId, userName);
136 }
137
138 /**
139 * Rule param was changed
140 */
141 public void ruleParamChanged(int profileId, int activeRuleId, String paramKey, String oldValue, String newValue, String userName) {
142 ActiveRule activeRule = getSession().getEntity(ActiveRule.class, activeRuleId);
143 RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId);
144
145 ruleParamChanged(profile, activeRule.getRule(), paramKey, oldValue, newValue, userName);
146
147 // Notify child profiles
148 activatedOrChanged(profileId, activeRuleId, userName);
149 }
150
151 /**
152 * Rule severity was changed
153 */
154 public void ruleSeverityChanged(int profileId, int activeRuleId, RulePriority oldSeverity, RulePriority newSeverity, String userName) {
155 ActiveRule activeRule = getSession().getEntity(ActiveRule.class, activeRuleId);
156 RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId);
157
158 ruleSeverityChanged(profile, activeRule.getRule(), oldSeverity, newSeverity, userName);
159
160 // Notify child profiles
161 activatedOrChanged(profileId, activeRuleId, userName);
162 }
163
164 /**
165 * Rule was activated/changed in parent profile.
166 */
167 private void activatedOrChanged(int parentProfileId, int activeRuleId, String userName) {
168 ActiveRule parentActiveRule = getSession().getEntity(ActiveRule.class, activeRuleId);
169 if (parentActiveRule.isInherited()) {
170 parentActiveRule.setInheritance(ActiveRule.OVERRIDES);
171 getSession().saveWithoutFlush(parentActiveRule);
172 }
173 for (RulesProfile child : getChildren(parentProfileId)) {
174 activateOrChange(child, parentActiveRule, userName);
175 }
176 getSession().commit();
177 }
178
179 /**
180 * Rule was deactivated in parent profile.
181 */
182 public void deactivated(int parentProfileId, int deactivatedRuleId, String userName) {
183 ActiveRule parentActiveRule = getSession().getEntity(ActiveRule.class, deactivatedRuleId);
184 RulesProfile profile = getSession().getEntity(RulesProfile.class, parentProfileId);
185 ruleDisabled(profile, parentActiveRule, userName);
186 for (RulesProfile child : getChildren(parentProfileId)) {
187 deactivate(child, parentActiveRule.getRule(), userName);
188 }
189 getSession().commit();
190 }
191
192 /**
193 * @return true, if setting <code>childProfile</code> as a child of <code>parentProfile</code> adds cycle
194 */
195 boolean isCycle(RulesProfile childProfile, RulesProfile parentProfile) {
196 while (parentProfile != null) {
197 if (childProfile.equals(parentProfile)) {
198 return true;
199 }
200 parentProfile = getParentProfile(parentProfile);
201 }
202 return false;
203 }
204
205 public void revert(int profileId, int activeRuleId, String userName) {
206 RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId);
207 ActiveRule oldActiveRule = getSession().getEntity(ActiveRule.class, activeRuleId);
208 if (oldActiveRule != null && oldActiveRule.doesOverride()) {
209 ActiveRule parentActiveRule = getParentProfile(profile).getActiveRule(oldActiveRule.getRule());
210 removeActiveRule(profile, oldActiveRule);
211 ActiveRule newActiveRule = (ActiveRule) parentActiveRule.clone();
212 newActiveRule.setRulesProfile(profile);
213 newActiveRule.setInheritance(ActiveRule.INHERITED);
214 profile.addActiveRule(newActiveRule);
215 getSession().saveWithoutFlush(newActiveRule);
216
217 // Compute change
218 ruleChanged(profile, oldActiveRule, newActiveRule, userName);
219
220 for (RulesProfile child : getChildren(profile)) {
221 activateOrChange(child, newActiveRule, userName);
222 }
223
224 getSession().commit();
225 }
226 }
227
228 private synchronized void incrementProfileVersionIfNeeded(RulesProfile profile) {
229 if (profile.getUsed()) {
230 profile.setVersion(profile.getVersion() + 1);
231 profile.setUsed(false);
232 getSession().saveWithoutFlush(profile);
233 }
234 }
235
236 /**
237 * Deal with creation of ActiveRuleChange item when a rule param is changed on a profile
238 */
239 private void ruleParamChanged(RulesProfile profile, Rule rule, String paramKey, String oldValue, String newValue, String userName) {
240 incrementProfileVersionIfNeeded(profile);
241 ActiveRuleChange rc = new ActiveRuleChange(userName, profile, rule);
242 if (!StringUtils.equals(oldValue, newValue)) {
243 rc.setParameterChange(paramKey, oldValue, newValue);
244 getSession().saveWithoutFlush(rc);
245 }
246 }
247
248 /**
249 * Deal with creation of ActiveRuleChange item when a rule severity is changed on a profile
250 */
251 private void ruleSeverityChanged(RulesProfile profile, Rule rule, RulePriority oldSeverity, RulePriority newSeverity, String userName) {
252 incrementProfileVersionIfNeeded(profile);
253 ActiveRuleChange rc = new ActiveRuleChange(userName, profile, rule);
254 if (!ObjectUtils.equals(oldSeverity, newSeverity)) {
255 rc.setOldSeverity(oldSeverity);
256 rc.setNewSeverity(newSeverity);
257 getSession().saveWithoutFlush(rc);
258 }
259 }
260
261 /**
262 * Deal with creation of ActiveRuleChange item when a rule is changed (severity and/or param(s)) on a profile
263 */
264 private void ruleChanged(RulesProfile profile, ActiveRule oldActiveRule, ActiveRule newActiveRule, String userName) {
265 incrementProfileVersionIfNeeded(profile);
266 ActiveRuleChange rc = new ActiveRuleChange(userName, profile, newActiveRule.getRule());
267
268 if (oldActiveRule.getSeverity() != newActiveRule.getSeverity()) {
269 rc.setOldSeverity(oldActiveRule.getSeverity());
270 rc.setNewSeverity(newActiveRule.getSeverity());
271 }
272 if (oldActiveRule.getRule().getParams() != null) {
273 for (RuleParam p : oldActiveRule.getRule().getParams()) {
274 String oldParam = oldActiveRule.getParameter(p.getKey());
275 String newParam = newActiveRule.getParameter(p.getKey());
276 if (!StringUtils.equals(oldParam, newParam)) {
277 rc.setParameterChange(p.getKey(), oldParam, newParam);
278 }
279 }
280 }
281
282 getSession().saveWithoutFlush(rc);
283 }
284
285 /**
286 * Deal with creation of ActiveRuleChange item when a rule is enabled on a profile
287 */
288 private void ruleEnabled(RulesProfile profile, ActiveRule newActiveRule, String userName) {
289 incrementProfileVersionIfNeeded(profile);
290 ActiveRuleChange rc = new ActiveRuleChange(userName, profile, newActiveRule.getRule());
291 rc.setEnabled(true);
292 rc.setNewSeverity(newActiveRule.getSeverity());
293 if (newActiveRule.getRule().getParams() != null) {
294 for (RuleParam p : newActiveRule.getRule().getParams()) {
295 String newParam = newActiveRule.getParameter(p.getKey());
296 if (newParam != null) {
297 rc.setParameterChange(p.getKey(), null, newParam);
298 }
299 }
300 }
301 getSession().saveWithoutFlush(rc);
302 }
303
304 /**
305 * Deal with creation of ActiveRuleChange item when a rule is disabled on a profile
306 */
307 private void ruleDisabled(RulesProfile profile, ActiveRule disabledRule, String userName) {
308 incrementProfileVersionIfNeeded(profile);
309 ActiveRuleChange rc = new ActiveRuleChange(userName, profile, disabledRule.getRule());
310 rc.setEnabled(false);
311 rc.setOldSeverity(disabledRule.getSeverity());
312 if (disabledRule.getRule().getParams() != null) {
313 for (RuleParam p : disabledRule.getRule().getParams()) {
314 String oldParam = disabledRule.getParameter(p.getKey());
315 if (oldParam != null) {
316 rc.setParameterChange(p.getKey(), oldParam, null);
317 }
318 }
319 }
320 getSession().saveWithoutFlush(rc);
321 }
322
323 private void activateOrChange(RulesProfile profile, ActiveRule parentActiveRule, String userName) {
324 ActiveRule oldActiveRule = profile.getActiveRule(parentActiveRule.getRule());
325 if (oldActiveRule != null) {
326 if (oldActiveRule.isInherited()) {
327 removeActiveRule(profile, oldActiveRule);
328 } else {
329 oldActiveRule.setInheritance(ActiveRule.OVERRIDES);
330 getSession().saveWithoutFlush(oldActiveRule);
331 return; // no need to change in children
332 }
333 }
334 ActiveRule newActiveRule = (ActiveRule) parentActiveRule.clone();
335 newActiveRule.setRulesProfile(profile);
336 newActiveRule.setInheritance(ActiveRule.INHERITED);
337 profile.addActiveRule(newActiveRule);
338 getSession().saveWithoutFlush(newActiveRule);
339
340 if (oldActiveRule != null) {
341 ruleChanged(profile, oldActiveRule, newActiveRule, userName);
342 } else {
343 ruleEnabled(profile, newActiveRule, userName);
344 }
345
346 for (RulesProfile child : getChildren(profile)) {
347 activateOrChange(child, newActiveRule, userName);
348 }
349 }
350
351 private void deactivate(RulesProfile profile, Rule rule, String userName) {
352 ActiveRule activeRule = profile.getActiveRule(rule);
353 if (activeRule != null) {
354 if (activeRule.isInherited()) {
355 ruleDisabled(profile, activeRule, userName);
356 removeActiveRule(profile, activeRule);
357 } else {
358 activeRule.setInheritance(null);
359 getSession().saveWithoutFlush(activeRule);
360 return; // no need to change in children
361 }
362
363 for (RulesProfile child : getChildren(profile)) {
364 deactivate(child, rule, userName);
365 }
366 }
367 }
368
369 private List<RulesProfile> getChildren(int parentId) {
370 RulesProfile parent = getSession().getEntity(RulesProfile.class, parentId);
371 return getChildren(parent);
372 }
373
374 private List<RulesProfile> getChildren(RulesProfile parent) {
375 return getSession().getResults(RulesProfile.class,
376 "language", parent.getLanguage(),
377 "parentName", parent.getName(),
378 "provided", false,
379 "enabled", true);
380 }
381
382 private void removeActiveRule(RulesProfile profile, ActiveRule activeRule) {
383 profile.removeActiveRule(activeRule);
384 getSession().removeWithoutFlush(activeRule);
385 }
386
387 RulesProfile getProfile(String language, String name) {
388 return getSession().getSingleResult(RulesProfile.class,
389 "language", language,
390 "name", name,
391 "enabled", true);
392 }
393
394 RulesProfile getParentProfile(RulesProfile profile) {
395 if (profile.getParentName() == null) {
396 return null;
397 }
398 return getProfile(profile.getLanguage(), profile.getParentName());
399 }
400
401 }