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 }