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    }