diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-10-14 10:16:08 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-10-14 10:16:08 +0200 |
commit | bb621a7a349ddf4568d3ab592fe5a40d0297e6be (patch) | |
tree | 0cf7ab245cb6487b134be7a93e6c5452730a10b2 | |
parent | d65c9957139e4b0da94f5cf797dfe98cfd62ce77 (diff) | |
parent | 3ac8de59552f3d38f9b952079cbffa8572840731 (diff) | |
download | sonarqube-bb621a7a349ddf4568d3ab592fe5a40d0297e6be.tar.gz sonarqube-bb621a7a349ddf4568d3ab592fe5a40d0297e6be.zip |
Merge remote-tracking branch 'origin/branch-4.5'
Conflicts:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
sonar-application/src/main/assembly/conf/sonar.properties
sonar-application/src/test/java/org/sonar/application/JdbcSettingsTest.java
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
33 files changed, 652 insertions, 457 deletions
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index 148a6df0b7c..d28dc487dec 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -20,17 +20,8 @@ package org.sonar.xoo; import org.sonar.api.SonarPlugin; -import org.sonar.xoo.lang.CoveragePerTestSensor; -import org.sonar.xoo.lang.MeasureSensor; -import org.sonar.xoo.lang.SymbolReferencesSensor; -import org.sonar.xoo.lang.SyntaxHighlightingSensor; -import org.sonar.xoo.lang.TestCaseSensor; -import org.sonar.xoo.lang.XooTokenizerSensor; -import org.sonar.xoo.rule.CreateIssueByInternalKeySensor; -import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; -import org.sonar.xoo.rule.OneIssuePerLineSensor; -import org.sonar.xoo.rule.XooQualityProfile; -import org.sonar.xoo.rule.XooRulesDefinition; +import org.sonar.xoo.lang.*; +import org.sonar.xoo.rule.*; import org.sonar.xoo.scm.XooBlameCommand; import org.sonar.xoo.scm.XooScmProvider; @@ -52,6 +43,10 @@ public class XooPlugin extends SonarPlugin { XooRulesDefinition.class, XooQualityProfile.class, + XooFakeExporter.class, + XooFakeImporter.class, + XooFakeImporterWithMessages.class, + // SCM XooScmProvider.class, XooBlameCommand.class, @@ -67,7 +62,7 @@ public class XooPlugin extends SonarPlugin { OneIssuePerLineSensor.class, OneIssueOnDirPerFileSensor.class, CreateIssueByInternalKeySensor.class - ); + ); } } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeExporter.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeExporter.java new file mode 100644 index 00000000000..b8375db463f --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeExporter.java @@ -0,0 +1,55 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.xoo.rule; + +import org.sonar.api.profiles.ProfileExporter; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.xoo.Xoo; + +import java.io.IOException; +import java.io.Writer; + +/** + * Fake exporter just for test + */ +public class XooFakeExporter extends ProfileExporter { + public XooFakeExporter() { + super("XooFakeExporter", "Xoo Fake Exporter"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[]{Xoo.KEY}; + } + + @Override + public String getMimeType() { + return "plain/custom"; + } + + @Override + public void exportProfile(RulesProfile profile, Writer writer) { + try { + writer.write("xoo -> " + profile.getName() + " -> " + profile.getActiveRules().size()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeImporter.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeImporter.java new file mode 100644 index 00000000000..867c11100dc --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeImporter.java @@ -0,0 +1,50 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.xoo.rule; + +import org.sonar.api.profiles.ProfileImporter; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.xoo.Xoo; + +import java.io.Reader; + +/** + * Fake importer just for test, it will NOT take into account the given file but will create some hard-coded rules + */ +public class XooFakeImporter extends ProfileImporter { + public XooFakeImporter() { + super("XooProfileImporter", "Xoo Profile Importer"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[] {Xoo.KEY}; + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + RulesProfile rulesProfile = RulesProfile.create(); + rulesProfile.activateRule(Rule.create(XooRulesDefinition.XOO_REPOSITORY, "x1"), RulePriority.CRITICAL); + return rulesProfile; + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeImporterWithMessages.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeImporterWithMessages.java new file mode 100644 index 00000000000..a11e3a2cca0 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooFakeImporterWithMessages.java @@ -0,0 +1,47 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.xoo.rule; + +import org.sonar.api.profiles.ProfileImporter; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.utils.ValidationMessages; + +import java.io.Reader; + +/** + * Fake importer just for test, it will NOT take into account the given file but will display some info and warning messages + */ +public class XooFakeImporterWithMessages extends ProfileImporter { + public XooFakeImporterWithMessages() { + super("XooFakeImporterWithMessages", "Xoo Profile Importer With Messages"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[] {}; + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + messages.addWarningText("a warning"); + messages.addInfoText("an info"); + return RulesProfile.create(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java index b49fc88edee..d0724aaa813 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SelectImpl.java @@ -21,7 +21,6 @@ package org.sonar.server.db.migrations; import org.apache.commons.dbutils.DbUtils; import org.sonar.core.persistence.Database; -import org.sonar.core.persistence.dialect.MySql; import java.sql.Connection; import java.sql.PreparedStatement; @@ -83,11 +82,7 @@ class SelectImpl extends BaseSqlStatement<Select> implements Select { static SelectImpl create(Database db, Connection connection, String sql) throws SQLException { PreparedStatement pstmt = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - if (db.getDialect().getId().equals(MySql.ID)) { - pstmt.setFetchSize(Integer.MIN_VALUE); - } else { - pstmt.setFetchSize(1000); - } + pstmt.setFetchSize(1000); return new SelectImpl(pstmt); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 2a917a3c4ad..381586b49fd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -357,7 +357,6 @@ class ServerComponents { pico.addSingleton(QProfileLookup.class); pico.addSingleton(QProfileProjectOperations.class); pico.addSingleton(QProfileProjectLookup.class); - pico.addSingleton(QProfileRepositoryExporter.class); pico.addSingleton(BuiltInProfiles.class); pico.addSingleton(QProfileRestoreBuiltInAction.class); pico.addSingleton(QProfilesWs.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java index d8a224f8957..0651346fcd4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileExporters.java @@ -20,15 +20,22 @@ package org.sonar.server.qualityprofile; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; import org.sonar.api.ServerComponent; import org.sonar.api.profiles.ProfileExporter; +import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rules.ActiveRuleParam; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; +import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; @@ -39,16 +46,20 @@ public class QProfileExporters implements ServerComponent { private final QProfileLoader loader; private final RuleFinder ruleFinder; + private final RuleActivator ruleActivator; private final ProfileExporter[] exporters; + private final ProfileImporter[] importers; - public QProfileExporters(QProfileLoader loader, RuleFinder ruleFinder, ProfileExporter[] exporters) { + public QProfileExporters(QProfileLoader loader, RuleFinder ruleFinder, RuleActivator ruleActivator, ProfileExporter[] exporters, ProfileImporter[] importers) { this.loader = loader; this.ruleFinder = ruleFinder; + this.ruleActivator = ruleActivator; this.exporters = exporters; + this.importers = importers; } - public QProfileExporters(QProfileLoader loader, RuleFinder ruleFinder) { - this(loader, ruleFinder, new ProfileExporter[0]); + public QProfileExporters(QProfileLoader loader, RuleFinder ruleFinder, RuleActivator ruleActivator) { + this(loader, ruleFinder, ruleActivator, new ProfileExporter[0], new ProfileImporter[0]); } public List<ProfileExporter> exportersForLanguage(String language) { @@ -104,4 +115,60 @@ public class QProfileExporters implements ServerComponent { } throw new NotFoundException("Unknown quality profile exporter: " + exporterKey); } + + /** + * Used by rails + */ + public List<ProfileImporter> findProfileImportersForLanguage(String language) { + List<ProfileImporter> result = new ArrayList<ProfileImporter>(); + for (ProfileImporter importer : importers) { + if (importer.getSupportedLanguages() == null || importer.getSupportedLanguages().length == 0 || ArrayUtils.contains(importer.getSupportedLanguages(), language)) { + result.add(importer); + } + } + return result; + } + + public QProfileResult importXml(QualityProfileDto profileDto, String importerKey, String xml, DbSession dbSession) { + QProfileResult result = new QProfileResult(); + ValidationMessages messages = ValidationMessages.create(); + ProfileImporter importer = getProfileImporter(importerKey); + RulesProfile rulesProfile = importer.importProfile(new StringReader(xml), messages); + importProfile(profileDto, rulesProfile, dbSession); + processValidationMessages(messages, result); + return result; + } + + private void importProfile(QualityProfileDto profileDto, RulesProfile rulesProfile, DbSession dbSession) { + for (org.sonar.api.rules.ActiveRule activeRule : rulesProfile.getActiveRules()) { + ruleActivator.activate(dbSession, toRuleActivation(activeRule), profileDto); + } + } + + private ProfileImporter getProfileImporter(String importerKey) { + for (ProfileImporter importer : importers) { + if (StringUtils.equals(importerKey, importer.getKey())) { + return importer; + } + } + throw new BadRequestException("No such importer : " + importerKey); + } + + private void processValidationMessages(ValidationMessages messages, QProfileResult result) { + if (!messages.getErrors().isEmpty()) { + throw new BadRequestException(messages); + } + result.addWarnings(messages.getWarnings()); + result.addInfos(messages.getInfos()); + } + + private RuleActivation toRuleActivation(org.sonar.api.rules.ActiveRule activeRule) { + RuleActivation ruleActivation = new RuleActivation(activeRule.getRule().ruleKey()); + ruleActivation.setSeverity(activeRule.getSeverity().name()); + for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) { + ruleActivation.setParameter(activeRuleParam.getKey(), activeRuleParam.getValue()); + } + return ruleActivation; + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileName.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileName.java index ae09c899cd5..d794058ea97 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileName.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileName.java @@ -37,6 +37,10 @@ public class QProfileName { return name; } + public static QProfileName createFor(String lang, String name){ + return new QProfileName(lang, name); + } + @Override public boolean equals(@Nullable Object o) { if (this == o) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java deleted file mode 100644 index 883d05675c7..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRepositoryExporter.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.qualityprofile; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import org.apache.commons.lang.ArrayUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.ibatis.session.SqlSession; -import org.sonar.api.ServerComponent; -import org.sonar.api.profiles.ProfileImporter; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.ActiveRuleParam; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.utils.ValidationMessages; -import org.sonar.core.qualityprofile.db.ActiveRuleDao; -import org.sonar.core.qualityprofile.db.ActiveRuleDto; -import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; -import org.sonar.server.exceptions.BadRequestException; - -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; - -import static com.google.common.collect.Lists.newArrayList; - -/** - * Used through ruby code <pre>Internal.profile_exporter</pre> - */ -public class QProfileRepositoryExporter implements ServerComponent { - - private final ActiveRuleDao activeRuleDao; - private final List<ProfileImporter> importers; - - /** - * Used by pico when no plugin provide profile exporter / importer - */ - public QProfileRepositoryExporter(ActiveRuleDao activeRuleDao) { - this(activeRuleDao, Lists.<ProfileImporter>newArrayList()); - } - - public QProfileRepositoryExporter(ActiveRuleDao activeRuleDao, List<ProfileImporter> importers) { - this.activeRuleDao = activeRuleDao; - this.importers = importers; - } - - public QProfileResult importXml(QProfile profile, String pluginKey, String xml, SqlSession session) { - QProfileResult result = new QProfileResult(); - ValidationMessages messages = ValidationMessages.create(); - ProfileImporter importer = getProfileImporter(pluginKey); - RulesProfile rulesProfile = importer.importProfile(new StringReader(xml), messages); - importProfile(profile.id(), rulesProfile, session); - processValidationMessages(messages, result); - return result; - } - - private void importProfile(int profileId, RulesProfile rulesProfile, SqlSession sqlSession) { - List<ActiveRuleDto> activeRuleDtos = newArrayList(); - Multimap<Integer, ActiveRuleParamDto> paramsByActiveRule = ArrayListMultimap.create(); - for (ActiveRule activeRule : rulesProfile.getActiveRules()) { - ActiveRuleDto activeRuleDto = toActiveRuleDto(activeRule, profileId); - activeRuleDao.insert(activeRuleDto, sqlSession); - activeRuleDtos.add(activeRuleDto); - for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) { - ActiveRuleParamDto activeRuleParamDto = toActiveRuleParamDto(activeRuleParam, activeRuleDto); - activeRuleDao.insert(activeRuleParamDto, sqlSession); - paramsByActiveRule.put(activeRuleDto.getId(), activeRuleParamDto); - } - } - // TODO use RuleActivator to benefit from changelog and preview cache cleanup - } - - private void processValidationMessages(ValidationMessages messages, QProfileResult result) { - if (!messages.getErrors().isEmpty()) { - throw new BadRequestException(messages); - } - result.addWarnings(messages.getWarnings()); - result.addInfos(messages.getInfos()); - } - - private ActiveRuleDto toActiveRuleDto(ActiveRule activeRule, int profileId) { - return new ActiveRuleDto() - .setProfileId(profileId) - .setRuleId(activeRule.getRule().getId()) - .setSeverity(toSeverityLevel(activeRule.getSeverity())); - } - - private String toSeverityLevel(RulePriority rulePriority) { - return rulePriority.name(); - } - - private ActiveRuleParamDto toActiveRuleParamDto(ActiveRuleParam activeRuleParam, ActiveRuleDto activeRuleDto) { - return new ActiveRuleParamDto() - .setActiveRuleId(activeRuleDto.getId()) - .setRulesParameterId(activeRuleParam.getRuleParam().getId()) - .setKey(activeRuleParam.getKey()) - .setValue(activeRuleParam.getValue()); - } - - private ProfileImporter getProfileImporter(String importerKey) { - for (ProfileImporter importer : importers) { - if (StringUtils.equals(importerKey, importer.getKey())) { - return importer; - } - } - throw new BadRequestException("No such importer : " + importerKey); - } - - public List<ProfileImporter> getProfileImportersForLanguage(String language) { - List<ProfileImporter> result = new ArrayList<ProfileImporter>(); - for (ProfileImporter importer : importers) { - if (importer.getSupportedLanguages() == null || importer.getSupportedLanguages().length == 0 || ArrayUtils.contains(importer.getSupportedLanguages(), language)) { - result.add(importer); - } - } - return result; - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java index 81fbb63faa3..5873862f58d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResult.java @@ -20,6 +20,8 @@ package org.sonar.server.qualityprofile; +import org.sonar.core.qualityprofile.db.QualityProfileDto; + import java.util.List; import static com.google.common.collect.Lists.newArrayList; @@ -29,7 +31,7 @@ public class QProfileResult { private List<String> warnings; private List<String> infos; - private QProfile profile; + private QualityProfileDto profile; public QProfileResult() { warnings = newArrayList(); @@ -54,11 +56,11 @@ public class QProfileResult { return this; } - public QProfile profile() { + public QualityProfileDto profile() { return profile; } - public QProfileResult setProfile(QProfile profile) { + public QProfileResult setProfile(QualityProfileDto profile) { this.profile = profile; return this; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java index 43a10b1ed6b..0e0a7acb4ac 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java @@ -48,6 +48,7 @@ import java.io.StringWriter; import java.io.Writer; import java.util.Collection; import java.util.List; +import java.util.Map; public class QProfileService implements ServerComponent { @@ -58,9 +59,10 @@ public class QProfileService implements ServerComponent { private final QProfileBackuper backuper; private final QProfileCopier copier; private final QProfileReset reset; + private final QProfileExporters exporters; public QProfileService(DbClient db, IndexClient index, RuleActivator ruleActivator, QProfileFactory factory, - QProfileBackuper backuper, QProfileCopier copier, QProfileReset reset) { + QProfileBackuper backuper, QProfileCopier copier, QProfileReset reset, QProfileExporters exporters) { this.db = db; this.index = index; this.ruleActivator = ruleActivator; @@ -68,15 +70,23 @@ public class QProfileService implements ServerComponent { this.backuper = backuper; this.copier = copier; this.reset = reset; + this.exporters = exporters; } - public QualityProfileDto create(QProfileName name) { + public QProfileResult create(QProfileName name, @Nullable Map<String, String> xmlQProfilesByPlugin) { verifyAdminPermission(); DbSession dbSession = db.openSession(false); try { + QProfileResult result = new QProfileResult(); QualityProfileDto profile = factory.create(dbSession, name); + result.setProfile(profile); + if (xmlQProfilesByPlugin != null) { + for (Map.Entry<String, String> entry : xmlQProfilesByPlugin.entrySet()) { + result.add(exporters.importXml(profile, entry.getKey(), entry.getValue(), dbSession)); + } + } dbSession.commit(); - return profile; + return result; } finally { dbSession.close(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java index 6bf1db54f80..89e8e6631a6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java @@ -88,6 +88,11 @@ public class RuleActivator implements ServerComponent { return doActivate(dbSession, activation, context); } + List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation, QualityProfileDto profileDto) { + RuleActivatorContext context = contextFactory.create(profileDto, activation.getRuleKey(), dbSession); + return doActivate(dbSession, activation, context); + } + private List<ActiveRuleChange> doActivate(DbSession dbSession, RuleActivation activation, RuleActivatorContext context) { context.verifyForActivation(); List<ActiveRuleChange> changes = Lists.newArrayList(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContextFactory.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContextFactory.java index 1854942476b..184d3fdf187 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContextFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContextFactory.java @@ -47,7 +47,7 @@ public class RuleActivatorContextFactory implements ServerComponent { throw new BadRequestException("Quality profile not found: " + profileKey); } context.setProfile(profile); - return create(profile, ruleKey, session, context); + return create(ruleKey, session, context); } RuleActivatorContext create(QProfileName profileName, RuleKey ruleKey, DbSession session) { @@ -57,13 +57,17 @@ public class RuleActivatorContextFactory implements ServerComponent { throw new BadRequestException("Quality profile not found: " + profileName); } context.setProfile(profile); - return create(profile, ruleKey, session, context); + return create(ruleKey, session, context); } - private RuleActivatorContext create(QualityProfileDto profile, RuleKey ruleKey, DbSession session, RuleActivatorContext context) { + RuleActivatorContext create(QualityProfileDto profile, RuleKey ruleKey, DbSession session) { + return create(ruleKey, session, new RuleActivatorContext().setProfile(profile)); + } + + private RuleActivatorContext create(RuleKey ruleKey, DbSession session, RuleActivatorContext context) { initRule(ruleKey, context, session); - initActiveRules(profile.getKey(), ruleKey, context, session, false); - String parentKee = profile.getParentKee(); + initActiveRules(context.profile().getKey(), ruleKey, context, session, false); + String parentKee = context.profile().getParentKee(); if (parentKee != null) { initActiveRules(parentKee, ruleKey, context, session, true); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java index fd8a87a9dd2..8c0f19312c8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileExportersTest.java @@ -19,23 +19,32 @@ */ package org.sonar.server.qualityprofile; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.ProfileExporter; +import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.tester.ServerTester; import java.io.IOException; +import java.io.Reader; import java.io.Writer; +import java.util.List; import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Fail.fail; @@ -45,9 +54,26 @@ public class QProfileExportersTest { @ClassRule public static ServerTester tester = new ServerTester().addXoo().addComponents( XooRulesDefinition.class, XooProfileDefinition.class, - XooExporter.class, StandardExporter.class); + XooExporter.class, StandardExporter.class, + XooProfileImporter.class, XooProfileImporterWithMessages.class, XooProfileImporterWithError.class); - QProfileExporters exporters = tester.get(QProfileExporters.class); + DbClient db; + DbSession dbSession; + QProfileExporters exporters; + QProfileLoader loader; + + @Before + public void before() { + db = tester.get(DbClient.class); + dbSession = db.openSession(false); + exporters = tester.get(QProfileExporters.class); + loader = tester.get(QProfileLoader.class); + } + + @After + public void after() throws Exception { + dbSession.close(); + } @Test public void exportersForLanguage() throws Exception { @@ -92,6 +118,58 @@ public class QProfileExportersTest { } } + @Test + public void profile_importers_for_language() throws Exception { + assertThat(exporters.findProfileImportersForLanguage("xoo")).hasSize(3); + } + + @Test + public void import_xml() throws Exception { + QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor("xoo", "import_xml"), "import_xml"); + db.qualityProfileDao().insert(dbSession, profileDto); + dbSession.commit(); + + assertThat(loader.findActiveRulesByProfile(profileDto.getKey())).isEmpty(); + + exporters.importXml(profileDto, "XooProfileImporter", "<xml/>", dbSession); + dbSession.commit(); + + List<ActiveRule> activeRules = loader.findActiveRulesByProfile(profileDto.getKey()); + assertThat(activeRules).hasSize(1); + ActiveRule activeRule = activeRules.get(0); + assertThat(activeRule.key().ruleKey()).isEqualTo(RuleKey.of("xoo", "R1")); + assertThat(activeRule.severity()).isEqualTo(Severity.CRITICAL); + } + + @Test + public void import_xml_return_messages() throws Exception { + QProfileResult result = exporters.importXml(QProfileTesting.newXooP1(), "XooProfileImporterWithMessages", "<xml/>", dbSession); + dbSession.commit(); + + assertThat(result.infos()).containsOnly("an info"); + assertThat(result.warnings()).containsOnly("a warning"); + } + + @Test + public void fail_to_import_xml_when_error_in_importer() throws Exception { + try { + exporters.importXml(QProfileTesting.newXooP1(), "XooProfileImporterWithError", "<xml/>", dbSession); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("error!"); + } + } + + @Test + public void fail_to_import_xml_on_unknown_importer() throws Exception { + try { + exporters.importXml(QProfileTesting.newXooP1(), "Unknown", "<xml/>", dbSession); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("No such importer : Unknown"); + } + } + public static class XooExporter extends ProfileExporter { public XooExporter() { super("xootool", "Xoo Tool"); @@ -157,4 +235,52 @@ public class QProfileExportersTest { } } + public static class XooProfileImporter extends ProfileImporter { + public XooProfileImporter() { + super("XooProfileImporter", "Xoo Profile Importer"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[] {"xoo"}; + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + RulesProfile rulesProfile = RulesProfile.create(); + rulesProfile.activateRule(Rule.create("xoo", "R1"), RulePriority.CRITICAL); + return rulesProfile; + } + } + + public static class XooProfileImporterWithMessages extends ProfileImporter { + public XooProfileImporterWithMessages() { + super("XooProfileImporterWithMessages", "Xoo Profile Importer With Message"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[] {}; + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + messages.addWarningText("a warning"); + messages.addInfoText("an info"); + return RulesProfile.create(); + } + } + + public static class XooProfileImporterWithError extends ProfileImporter { + public XooProfileImporterWithError() { + super("XooProfileImporterWithError", "Xoo Profile Importer With Error"); + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + messages.addErrorText("error!"); + return RulesProfile.create(); + } + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java deleted file mode 100644 index cdbc9cad7aa..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRepositoryExporterTest.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.qualityprofile; - -import org.apache.ibatis.session.SqlSession; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; -import org.sonar.api.database.DatabaseSession; -import org.sonar.api.profiles.ProfileImporter; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.utils.ValidationMessages; -import org.sonar.core.qualityprofile.db.ActiveRuleDao; -import org.sonar.core.qualityprofile.db.ActiveRuleDto; -import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; -import org.sonar.server.exceptions.BadRequestException; - -import java.io.Reader; -import java.util.List; - -import static com.google.common.collect.Lists.newArrayList; -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class QProfileRepositoryExporterTest { - - @Mock - SqlSession session; - - @Mock - DatabaseSession hibernateSession; - - @Mock - ActiveRuleDao activeRuleDao; - - List<ProfileImporter> importers = newArrayList(); - - Integer currentId = 1; - - QProfileRepositoryExporter operations; - - @Before - public void setUp() throws Exception { - // Associate an id when inserting an object to simulate the db id generator - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - ActiveRuleDto dto = (ActiveRuleDto) args[0]; - dto.setId(currentId++); - return null; - } - }).when(activeRuleDao).insert(any(ActiveRuleDto.class), any(SqlSession.class)); - - operations = new QProfileRepositoryExporter(activeRuleDao, importers); - } - - @Test - public void import_from_xml_plugin() throws Exception { - RulesProfile profile = RulesProfile.create("Default", "java"); - Rule rule = Rule.create("pmd", "rule1"); - rule.createParameter("max"); - rule.setId(10); - ActiveRule activeRule = profile.activateRule(rule, RulePriority.BLOCKER); - activeRule.setParameter("max", "10"); - - ProfileImporter importer = mock(ProfileImporter.class); - when(importer.getKey()).thenReturn("pmd"); - when(importer.importProfile(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile); - importers.add(importer); - - operations.importXml(new QProfile().setId(1), "pmd", "<xml/>", session); - - verify(importer).importProfile(any(Reader.class), any(ValidationMessages.class)); - - ArgumentCaptor<ActiveRuleDto> activeRuleArgument = ArgumentCaptor.forClass(ActiveRuleDto.class); - verify(activeRuleDao).insert(activeRuleArgument.capture(), eq(session)); - assertThat(activeRuleArgument.getValue().getRuleId()).isEqualTo(10); - assertThat(activeRuleArgument.getValue().getSeverityString()).isEqualTo(Severity.BLOCKER); - - ArgumentCaptor<ActiveRuleParamDto> activeRuleParamArgument = ArgumentCaptor.forClass(ActiveRuleParamDto.class); - verify(activeRuleDao).insert(activeRuleParamArgument.capture(), eq(session)); - assertThat(activeRuleParamArgument.getValue().getKey()).isEqualTo("max"); - assertThat(activeRuleParamArgument.getValue().getValue()).isEqualTo("10"); - } - - @Test - public void import_from_xml_plugin_add_infos_and_warnings() throws Exception { - final RulesProfile profile = RulesProfile.create("Default", "java"); - Rule rule = Rule.create("pmd", "rule1"); - rule.createParameter("max"); - rule.setId(10); - ActiveRule activeRule = profile.activateRule(rule, RulePriority.BLOCKER); - activeRule.setParameter("max", "10"); - - ProfileImporter importer = mock(ProfileImporter.class); - when(importer.getKey()).thenReturn("pmd"); - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - ValidationMessages validationMessages = (ValidationMessages) args[1]; - validationMessages.addInfoText("an info message"); - validationMessages.addWarningText("a warning message"); - return profile; - } - }).when(importer).importProfile(any(Reader.class), any(ValidationMessages.class)); - importers.add(importer); - - QProfileResult result = operations.importXml(new QProfile().setId(1), "pmd", "<xml/>", session); - ; - assertThat(result.infos()).hasSize(1); - assertThat(result.warnings()).hasSize(1); - } - - @Test - public void fail_to_import_profile_from_xml_plugin_if_error() throws Exception { - final RulesProfile profile = RulesProfile.create("Default", "java"); - Rule rule = Rule.create("pmd", "rule1"); - rule.createParameter("max"); - rule.setId(10); - ActiveRule activeRule = profile.activateRule(rule, RulePriority.BLOCKER); - activeRule.setParameter("max", "10"); - - ProfileImporter importer = mock(ProfileImporter.class); - when(importer.getKey()).thenReturn("pmd"); - importers.add(importer); - - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - ValidationMessages validationMessages = (ValidationMessages) args[1]; - validationMessages.addErrorText("error!"); - return profile; - } - }).when(importer).importProfile(any(Reader.class), any(ValidationMessages.class)); - - try { - operations.importXml(new QProfile().setId(1), "pmd", "<xml/>", session); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - } - } - - @Test - public void fail_to_import_profile_when_missing_importer() throws Exception { - final RulesProfile profile = RulesProfile.create("Default", "java"); - Rule rule = Rule.create("pmd", "rule1"); - rule.createParameter("max"); - rule.setId(10); - ActiveRule activeRule = profile.activateRule(rule, RulePriority.BLOCKER); - activeRule.setParameter("max", "10"); - - ProfileImporter importer = mock(ProfileImporter.class); - when(importer.getKey()).thenReturn("pmd"); - importers.add(importer); - - when(importer.importProfile(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile); - - try { - operations.importXml(new QProfile().setId(1), "unknown", "<xml/>", session); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("No such importer : unknown"); - } - verify(importer, never()).importProfile(any(Reader.class), any(ValidationMessages.class)); - } - - @Test - public void get_profile_importers_for_language() throws Exception { - // 2 importers not declaring supported languages -> match all languages -> to be include in result - ProfileImporter importersWithEmptySupportedLanguagesList = mock(ProfileImporter.class); - when(importersWithEmptySupportedLanguagesList.getSupportedLanguages()).thenReturn(new String[] {}); - importers.add(importersWithEmptySupportedLanguagesList); - importers.add(mock(ProfileImporter.class)); - - // 1 importers supporting the java language -> to be include in result - ProfileImporter importerSupportingJava = mock(ProfileImporter.class); - when(importerSupportingJava.getSupportedLanguages()).thenReturn(new String[] {"java"}); - importers.add(importerSupportingJava); - - // 1 importers supporting another language -> not to be include in result - ProfileImporter importerSupportingAnotherLanguage = mock(ProfileImporter.class); - when(importerSupportingAnotherLanguage.getSupportedLanguages()).thenReturn(new String[] {"js"}); - importers.add(importerSupportingAnotherLanguage); - - assertThat(operations.getProfileImportersForLanguage("java")).hasSize(3); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java index b39658b17f1..73387e8481a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java @@ -19,19 +19,27 @@ */ package org.sonar.server.qualityprofile; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.sonar.api.profiles.ProfileExporter; +import org.sonar.api.profiles.ProfileImporter; +import org.sonar.api.profiles.RulesProfile; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.utils.ValidationMessages; import org.sonar.core.activity.Activity; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.ActiveRuleKey; +import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.rule.RuleDto; import org.sonar.core.user.UserDto; import org.sonar.server.activity.ActivityService; @@ -44,6 +52,9 @@ import org.sonar.server.search.Result; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; import java.util.Date; import java.util.List; import java.util.Map; @@ -55,7 +66,7 @@ import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P2_KEY; public class QProfileServiceMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester().addComponents(XooProfileImporter.class, XooExporter.class); DbClient db; DbSession dbSession; @@ -88,6 +99,35 @@ public class QProfileServiceMediumTest { } @Test + public void create_profile() throws Exception { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("me"); + + QualityProfileDto profile = service.create(QProfileName.createFor("xoo", "New Profile"), null).profile(); + + assertThat(loader.getByKey(profile.getKey())).isNotNull(); + assertThat(loader.getByKey(profile.getKey()).getLanguage()).isEqualTo("xoo"); + assertThat(loader.getByKey(profile.getKey()).getName()).isEqualTo("New Profile"); + } + + @Test + public void create_profile_with_xml() throws Exception { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("me"); + + db.ruleDao().insert(dbSession, RuleTesting.newDto(RuleKey.of("xoo", "R1")).setLanguage("xoo").setSeverity("MINOR")); + dbSession.commit(); + + QProfileResult result = service.create(QProfileName.createFor("xoo", "New Profile"), ImmutableMap.of("XooProfileImporter", "<xml/>")); + QualityProfileDto profile = result.profile(); + + assertThat(loader.getByKey(profile.getKey())).isNotNull(); + assertThat(loader.getByKey(profile.getKey()).getLanguage()).isEqualTo("xoo"); + assertThat(loader.getByKey(profile.getKey()).getName()).isEqualTo("New Profile"); + + List<ActiveRule> activeRules = loader.findActiveRulesByProfile(profile.getKey()); + assertThat(activeRules).hasSize(1); + } + + @Test public void count_by_all_profiles() throws Exception { MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("me"); @@ -294,4 +334,51 @@ public class QProfileServiceMediumTest { assertThat(service.getDefault("xoo").getKey()).isEqualTo(XOO_P1_KEY); } + + public static class XooExporter extends ProfileExporter { + public XooExporter() { + super("xootool", "Xoo Tool"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[]{"xoo"}; + } + + @Override + public String getMimeType() { + return "plain/custom"; + } + + @Override + public void exportProfile(RulesProfile profile, Writer writer) { + try { + writer.write("xoo -> " + profile.getName() + " -> " + profile.getActiveRules().size()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + public static class XooProfileImporter extends ProfileImporter { + public XooProfileImporter() { + super("XooProfileImporter", "Xoo Profile Importer"); + } + + @Override + public String[] getSupportedLanguages() { + return new String[]{"xoo"}; + } + + @Override + public RulesProfile importProfile(Reader reader, ValidationMessages messages) { + RulesProfile rulesProfile = RulesProfile.create(); + Rule rule = Rule.create("xoo", "R1"); + rule.createParameter("acceptWhitespace"); + org.sonar.api.rules.ActiveRule activeRule = rulesProfile.activateRule(rule, RulePriority.CRITICAL); + activeRule.setParameter("acceptWhitespace", "true"); + return rulesProfile; + } + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java index ae5dc30e6ce..34e453b1f2d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java @@ -32,6 +32,7 @@ import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; +import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; import org.sonar.server.db.DbClient; @@ -69,6 +70,8 @@ public class RuleActivatorMediumTest { RuleActivator ruleActivator; ActiveRuleIndex index; + QualityProfileDto profileDto; + @Before public void before() { tester.clearDbAndIndexes(); @@ -100,7 +103,8 @@ public class RuleActivatorMediumTest { .setName("format").setDefaultValue("txt").setType(RuleParamType.STRING.type())); // create pre-defined profile P1 - db.qualityProfileDao().insert(dbSession, QProfileTesting.newXooP1()); + profileDto = QProfileTesting.newXooP1(); + db.qualityProfileDao().insert(dbSession, profileDto); dbSession.commit(); dbSession.clearCache(); } @@ -128,6 +132,23 @@ public class RuleActivatorMediumTest { } @Test + public void activate_with_profile_dto() throws Exception { + RuleActivation activation = new RuleActivation(RuleTesting.XOO_X1); + activation.setSeverity(Severity.BLOCKER); + activation.setParameter("max", "7"); + activation.setParameter("min", "3"); + List<ActiveRuleChange> changes = ruleActivator.activate(dbSession, activation, profileDto); + dbSession.commit(); + dbSession.clearCache(); + + assertThat(countActiveRules(XOO_P1_KEY)).isEqualTo(1); + verifyHasActiveRule(ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1), Severity.BLOCKER, null, + ImmutableMap.of("max", "7", "min", "3")); + assertThat(changes).hasSize(1); + assertThat(changes.get(0).getType()).isEqualTo(ActiveRuleChange.Type.ACTIVATED); + } + + @Test public void activate_with_default_severity_and_parameter() throws Exception { activate(new RuleActivation(RuleTesting.XOO_X1), XOO_P1_KEY); diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/components_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/components_controller.rb index a080bc1881d..410d59f601f 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/components_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/components_controller.rb @@ -27,9 +27,13 @@ class Api::ComponentsController < Api::ApiController search = params[:s] bad_request("Minimum search is #{ResourceIndex::MIN_SEARCH_SIZE} characters") if search.empty? || search.to_s.size<ResourceIndex::MIN_SEARCH_SIZE + # SONAR-5198 Escape '_' on Oracle and MsSQL + dialect = java_facade.getDatabase().getDialect().getId() + additional_escape = dialect == 'oracle' || dialect == 'mssql' ? "ESCAPE '\\'" : '' + key = escape_like(search).downcase results = ResourceIndex.all(:select => 'distinct(resource_id),root_project_id,qualifier,name_size', # optimization to not load unused columns like 'kee' - :conditions => ['kee like ?', key + '%'], + :conditions => ['kee like ? ' + additional_escape, key + '%'], :order => 'name_size') results = select_authorized(:user, results) diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb index 9f1be3c76b2..887e8e63797 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb @@ -170,7 +170,7 @@ class IssuesController < ApplicationController new_params = params.clone new_params.delete('controller') new_params.delete('action') - translate_unassigned(new_params) + new_params end def init_params @@ -181,16 +181,6 @@ class IssuesController < ApplicationController Internal.issues.sanitizeFilterQuery(params).to_hash end - def translate_unassigned(issues_query_params) - if issues_query_params.has_key?(:assignees) && issues_query_params[:assignees] == '<unassigned>' - issues_query_params.delete(:assignees) - issues_query_params[:assigned] = false - else - issues_query_params[:assigned] = nil - end - issues_query_params - end - def issues_query_params_from_filter(filter) Internal.issues.deserializeFilterQuery(filter).to_hash end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb index b6a5ba9f4e2..6956a1287eb 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb @@ -71,10 +71,9 @@ class ProfilesController < ApplicationController end end profile_name = Java::OrgSonarServerQualityprofile::QProfileName.new(params[:language], params[:name]) - Internal.qprofile_service.create(profile_name) - # TODO use files_by_key - #flash[:notice] = message('quality_profiles.profile_x_created', :params => result.profile.name) - #flash_result(result) + result = Internal.qprofile_service.create(profile_name, files_by_key) + flash[:notice] = message('quality_profiles.profile_x_created', :params => result.profile().getName()) + flash_result(result) end redirect_to :action => 'index' end @@ -530,7 +529,7 @@ class ProfilesController < ApplicationController def flash_result(result) # only 4 messages are kept each time to avoid cookie overflow. unless result.infos.empty? - flash[:notice] += result.infos.to_a[0...4].join('<br/>') + flash[:notice] += '<br/>' + result.infos.to_a[0...4].join('<br/>') end unless result.warnings.empty? flash[:warning] = result.warnings.to_a[0...4].join('<br/>') diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/_create_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/_create_form.html.erb index 328e2af6add..a003c054478 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/_create_form.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/_create_form.html.erb @@ -1,6 +1,6 @@ <% language = controller.java_facade.getLanguages().find { |l| l.getKey()==language_key } - importers = Internal.component(Java::OrgSonarServerQualityprofile::QProfileRepositoryExporter.java_class).getProfileImportersForLanguage(language_key) + importers = Internal.component(Java::OrgSonarServerQualityprofile::QProfileExporters.java_class).findProfileImportersForLanguage(language_key) %> <form id="create-profile-form" action="profiles/create" enctype="multipart/form-data" method="POST"> <fieldset> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/config/environment.rb b/server/sonar-web/src/main/webapp/WEB-INF/config/environment.rb index 81f897e66a3..62d3e809416 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/config/environment.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/config/environment.rb @@ -261,3 +261,9 @@ DatabaseVersion.automatic_setup # All these changes are prefixed by the comment #sonar # # + +# Increase size of form parameters +# See http://jira.codehaus.org/browse/SONAR-5577 +Rack::Utils.key_space_limit = 262144 # 4 times the default size + + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb new file mode 100644 index 00000000000..168fa378ebb --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb @@ -0,0 +1,44 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# SonarQube is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# +# SonarQube 4.5 +# SONAR-4950 Unique constraint cannot be created because it would be on resource_id that is nullable +# +class RemoveDuplicationInGroupRoles < ActiveRecord::Migration + + class GroupRole < ActiveRecord::Base + end + + def self.up + GroupRole.reset_column_information + + duplicated_ids = ActiveRecord::Base.connection.select_rows('select group_id,resource_id,role from group_roles group by group_id,resource_id,role having count(*) > 1') + say_with_time "Remove #{duplicated_ids.size} duplicated group roles" do + duplicated_ids.each do |fields| + rows = GroupRole.find(:all, :conditions => {:group_id => fields[0], :resource_id => fields[1], :role => fields[2]}) + # delete all rows except the last one + rows[0...-1].each do |row| + GroupRole.delete(row.id) + end + end + end + end +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb b/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb index 400ccbad2f1..61b266fc66d 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb @@ -64,6 +64,10 @@ class PluginRealm result = nil if !username.blank? && !password.blank? user = User.find_active_by_login(username) + # SONAR-4950 Use a transaction to prevent multiple insertion of same groups + User.transaction do + user.save(false) + end result = user if user && user.authenticated?(password) end result @@ -117,40 +121,46 @@ class PluginRealm def synchronize(username, password, details) username=details.getName() if username.blank? && details user = User.find_by_login(username) - if !user - # No such user in Sonar database - return nil if !Api::Utils.java_facade.getSettings().getBoolean('sonar.authenticator.createUsers') - # Automatically create a user in the sonar db if authentication has been successfully done - user = User.new(:login => username, :name => username, :email => '') - if details - user.name = details.getName() - user.email = details.getEmail() - end - default_group_name = Api::Utils.java_facade.getSettings().getString('sonar.defaultGroup') - default_group = Group.find_by_name(default_group_name) - if default_group - user.groups << default_group + + # SONAR-4950 Use a transaction to prevent multiple insertion of same groups + User.transaction do + if !user + # No such user in Sonar database + return nil if !Api::Utils.java_facade.getSettings().getBoolean('sonar.authenticator.createUsers') + # Automatically create a user in the sonar db if authentication has been successfully done + user = User.new(:login => username, :name => username, :email => '') + if details + user.name = details.getName() + user.email = details.getEmail() + end + default_group_name = Api::Utils.java_facade.getSettings().getString('sonar.defaultGroup') + default_group = Group.find_by_name(default_group_name) + if default_group + user.groups << default_group + else + Rails.logger.error("The default user group does not exist: #{default_group_name}. Please check the parameter 'Default user group' in general settings.") + end else - Rails.logger.error("The default user group does not exist: #{default_group_name}. Please check the parameter 'Default user group' in general settings.") + # Existing user + if details && Api::Utils.java_facade.getSettings().getBoolean('sonar.security.updateUserAttributes') + user.name = details.getName() + user.email = details.getEmail() + end end - else - # Existing user - if details && Api::Utils.java_facade.getSettings().getBoolean('sonar.security.updateUserAttributes') - user.name = details.getName() - user.email = details.getEmail() + if @save_password + user.password = password + user.password_confirmation = password end + + # A user that is synchronized with an external system is always set to 'active' (see SONAR-3258 for the deactivation concept) + user.active=true + # Note that validation disabled + user.save(false) + + synchronize_groups(user) + user.notify_creation_handlers + user end - if @save_password - user.password = password - user.password_confirmation = password - end - synchronize_groups(user) - # A user that is synchronized with an external system is always set to 'active' (see SONAR-3258 for the deactivation concept) - user.active=true - # Note that validation disabled - user.save(false) - user.notify_creation_handlers - user end def synchronize_groups(user) diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index b9bee4266e5..0c70358e1c1 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -28,7 +28,7 @@ #----- MySQL 5.x -#sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useCursorFetch=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance +#sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useCursorFetch=true #----- Oracle 10g/11g diff --git a/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java b/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java index 4a34e55a642..b2f36dbb6c0 100644 --- a/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java +++ b/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java @@ -95,6 +95,7 @@ public class JdbcSettings { if (Provider.MYSQL.equals(provider)) { checkRequiredParameter(url, "useUnicode=true"); checkRequiredParameter(url, "characterEncoding=utf8"); + checkRequiredParameter(url, "useCursorFetch=true"); checkRecommendedParameter(url, "rewriteBatchedStatements=true"); checkRecommendedParameter(url, "useConfigs=maxPerformance"); } diff --git a/sonar-application/src/test/java/org/sonar/application/JdbcSettingsTest.java b/sonar-application/src/test/java/org/sonar/application/JdbcSettingsTest.java index 5a2708ab5c8..d6fd2af70e2 100644 --- a/sonar-application/src/test/java/org/sonar/application/JdbcSettingsTest.java +++ b/sonar-application/src/test/java/org/sonar/application/JdbcSettingsTest.java @@ -65,11 +65,11 @@ public class JdbcSettingsTest { // minimal -> ok settings.checkUrlParameters(JdbcSettings.Provider.MYSQL, - "jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8"); + "jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&useCursorFetch=true"); // full -> ok settings.checkUrlParameters(JdbcSettings.Provider.MYSQL, - "jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance"); + "jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useCursorFetch=true"); // missing required -> ko try { diff --git a/sonar-core/src/main/resources/org/sonar/core/permission/PermissionTemplateMapper.xml b/sonar-core/src/main/resources/org/sonar/core/permission/PermissionTemplateMapper.xml index 64fb208e8e9..c153b3564fd 100644 --- a/sonar-core/src/main/resources/org/sonar/core/permission/PermissionTemplateMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/permission/PermissionTemplateMapper.xml @@ -73,6 +73,7 @@ AND ptu.permission_reference=#{query.permission} AND ptu.template_id=#{templateId} <where> + u.active = ${_true} <choose> <when test="query.membership() == 'IN'"> AND ptu.permission_reference IS NOT NULL diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index e7aa6e29d9a..2a6adb2b0b5 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -256,6 +256,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('583'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('584'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('600'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('601'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('602'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('700'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('701'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('702'); diff --git a/sonar-core/src/test/java/org/sonar/core/permission/UserWithPermissionTemplateDaoTest.java b/sonar-core/src/test/java/org/sonar/core/permission/UserWithPermissionTemplateDaoTest.java index ed0dc5358fb..e0f0aea1167 100644 --- a/sonar-core/src/test/java/org/sonar/core/permission/UserWithPermissionTemplateDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/permission/UserWithPermissionTemplateDaoTest.java @@ -20,11 +20,15 @@ package org.sonar.core.permission; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import org.junit.Before; import org.junit.Test; import org.sonar.api.utils.System2; import org.sonar.core.persistence.AbstractDaoTestCase; +import javax.annotation.Nullable; + import java.util.List; import static org.fest.assertions.Assertions.assertThat; @@ -99,6 +103,23 @@ public class UserWithPermissionTemplateDaoTest extends AbstractDaoTestCase { } @Test + public void select_only_enable_users() throws Exception { + setupData("select_only_enable_users"); + + PermissionQuery query = PermissionQuery.builder().permission("user").build(); + List<UserWithPermissionDto> result = dao.selectUsers(query, 999L); + assertThat(result).hasSize(3); + + // Disabled user should not be returned + assertThat(Iterables.find(result, new Predicate<UserWithPermissionDto>() { + @Override + public boolean apply(@Nullable UserWithPermissionDto input) { + return input.getLogin().equals("disabledUser"); + } + }, null)).isNull(); + } + + @Test public void search_by_user_name() throws Exception { setupData("users_with_permissions"); diff --git a/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/select_only_enable_users.xml b/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/select_only_enable_users.xml new file mode 100644 index 00000000000..14833919475 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/select_only_enable_users.xml @@ -0,0 +1,14 @@ +<dataset> + + <users id="200" login="user1" name="User1" active="[true]"/> + <users id="201" login="user2" name="User2" active="[true]"/> + <users id="202" login="user3" name="User3" active="[true]"/> + <users id="999" login="disabledUser" name="disabledUser" active="[false]"/> + + <perm_templates_users id="1" user_id="200" permission_reference="user" template_id="50"/> + <perm_templates_users id="2" user_id="200" permission_reference="admin" template_id="50"/> + <perm_templates_users id="3" user_id="200" permission_reference="codeviewer" template_id="50"/> + + <perm_templates_users id="4" user_id="201" permission_reference="user" template_id="50"/> + +</dataset> diff --git a/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions.xml b/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions.xml index 9c9f018aa2b..e886636bd9b 100644 --- a/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions.xml +++ b/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions.xml @@ -1,8 +1,8 @@ <dataset> - <users id="200" login="user1" name="User1"/> - <users id="201" login="user2" name="User2"/> - <users id="202" login="user3" name="User3"/> + <users id="200" login="user1" name="User1" active="[true]"/> + <users id="201" login="user2" name="User2" active="[true]"/> + <users id="202" login="user3" name="User3" active="[true]"/> <perm_templates_users id="1" user_id="200" permission_reference="user" template_id="50"/> <perm_templates_users id="2" user_id="200" permission_reference="admin" template_id="50"/> diff --git a/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions_should_be_sorted_by_user_name.xml b/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions_should_be_sorted_by_user_name.xml index 54acdf839b2..fd8b981c203 100644 --- a/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions_should_be_sorted_by_user_name.xml +++ b/sonar-core/src/test/resources/org/sonar/core/permission/UserWithPermissionTemplateDaoTest/users_with_permissions_should_be_sorted_by_user_name.xml @@ -1,8 +1,8 @@ <dataset> - <users id="200" login="user3" name="User3"/> - <users id="201" login="user1" name="User1"/> - <users id="202" login="user2" name="User2"/> + <users id="200" login="user3" name="User3" active="[true]"/> + <users id="201" login="user1" name="User1" active="[true]"/> + <users id="202" login="user2" name="User2" active="[true]"/> <perm_templates_users id="1" user_id="200" permission_reference="user" template_id="50"/> <perm_templates_users id="2" user_id="200" permission_reference="admin" template_id="50"/> |