From a505a98b4d1ad33c80b939ae6fed12043e635ded Mon Sep 17 00:00:00 2001 From: David Gageot Date: Thu, 18 Oct 2012 17:47:19 +0200 Subject: [PATCH] SONAR-3895 Local mode --- sonar-batch/pom.xml | 5 + .../batch/bootstrap/BootstrapModule.java | 8 +- .../org/sonar/batch/bootstrap/LocalMode.java | 50 +++++ .../org/sonar/batch/local/LocalDatabase.java | 125 +++++++++++ .../sonar/batch/bootstrap/LocalModeTest.java | 47 +++++ sonar-server/pom.xml | 5 + .../server/database/LocalDatabaseFactory.java | 199 ++++++++++++++++++ .../org/sonar/server/platform/Platform.java | 35 ++- .../app/controllers/api/synchro_controller.rb | 37 ++++ .../webapp/WEB-INF/app/models/active_rule.rb | 14 +- .../main/webapp/WEB-INF/app/models/profile.rb | 51 +++-- .../main/webapp/WEB-INF/app/models/rule.rb | 35 ++- 12 files changed, 560 insertions(+), 51 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrap/LocalMode.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/bootstrap/LocalModeTest.java create mode 100644 sonar-server/src/main/java/org/sonar/server/database/LocalDatabaseFactory.java create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index 7255d9719b2..6a1a222b901 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -56,6 +56,11 @@ commons-lang commons-lang + + com.google.code.gson + gson + 2.2.2 + diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java index 528186df9f4..f5bfb3eadd4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java @@ -30,6 +30,7 @@ import org.sonar.batch.RemoteServerMetadata; import org.sonar.batch.ServerMetadata; import org.sonar.batch.config.BatchDatabaseSettingsLoader; import org.sonar.batch.config.BatchSettings; +import org.sonar.batch.local.LocalDatabase; import org.sonar.core.config.Logback; import org.sonar.core.i18n.I18nManager; import org.sonar.core.i18n.RuleI18nManager; @@ -77,11 +78,15 @@ public class BootstrapModule extends Module { Thread.currentThread().setContextClassLoader(bootstrapClassLoader); addCoreSingleton(RemoteServerMetadata.class); + + // local mode + addCoreSingleton(LocalMode.class); + addCoreSingleton(LocalDatabase.class); + // mybatis addCoreSingleton(BatchDatabase.class); addCoreSingleton(MyBatis.class); addCoreSingleton(DatabaseVersion.class); - addCoreSingleton(DatabaseBatchCompatibility.class); for (Class daoClass : DaoUtils.getDaoClasses()) { addCoreSingleton(daoClass); } @@ -91,6 +96,7 @@ public class BootstrapModule extends Module { addCoreSingleton(ThreadLocalDatabaseSessionFactory.class); addAdapter(new DatabaseSessionProvider()); + addCoreSingleton(DatabaseBatchCompatibility.class); for (Object component : boostrapperComponents) { addCoreSingleton(component); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/LocalMode.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/LocalMode.java new file mode 100644 index 00000000000..618a1e764c7 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/LocalMode.java @@ -0,0 +1,50 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.bootstrap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; +import org.sonar.api.Property; +import org.sonar.api.config.Settings; + +/** + * @since 3.4 + */ +@Property(key = "sonar.local", defaultValue = "false", name = "Local Mode") +public class LocalMode implements BatchComponent { + private static final Logger LOG = LoggerFactory.getLogger(LocalMode.class); + + private final boolean enabled; + + public LocalMode(Settings settings) { + enabled = settings.getBoolean("sonar.local"); + } + + public boolean isEnabled() { + return enabled; + } + + public void start() { + if (enabled) { + LOG.info("Local Mode"); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java b/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java new file mode 100644 index 00000000000..a8e1877e6f6 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java @@ -0,0 +1,125 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.local; + +import com.google.common.io.Closeables; +import com.google.gson.Gson; +import org.apache.commons.dbcp.BasicDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; +import org.sonar.api.config.Settings; +import org.sonar.api.database.DatabaseProperties; +import org.sonar.api.platform.Server; +import org.sonar.api.utils.HttpDownloader; +import org.sonar.api.utils.SonarException; +import org.sonar.batch.bootstrap.LocalMode; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.sql.SQLException; + +/** + * @since 3.4 + */ +public class LocalDatabase implements BatchComponent { + private static final Logger LOG = LoggerFactory.getLogger(LocalDatabase.class); + + public static final String API_SYNCHRO = "/api/synchro"; + private static final String DIALECT = "h2"; + private static final String DRIVER = "org.h2.Driver"; + private static final String URL = "jdbc:h2:"; + private static final String USER = "sonar"; + private static final String PASSWORD = "sonar"; + + private final LocalMode localMode; + private final Settings settings; + private final Server server; + private final HttpDownloader httpDownloader; + private BasicDataSource dataSource; + + public LocalDatabase(LocalMode localMode, Settings settings, Server server, HttpDownloader httpDownloader) { + this.localMode = localMode; + this.settings = settings; + this.server = server; + this.httpDownloader = httpDownloader; + } + + public void start() { + if (!localMode.isEnabled()) { + return; + } + + LOG.info("Download database"); + Path path = downloadDatabase(); + + LOG.info("Starting local database"); + replaceSettings(path); + configureDataSource(path); + } + + private Path downloadDatabase() { + InputStream stream = null; + try { + stream = httpDownloader.openStream(URI.create(server.getURL() + API_SYNCHRO)); + return new Gson().fromJson(new InputStreamReader(stream), Path.class); + } finally { + Closeables.closeQuietly(stream); + } + } + + static class Path { + String path; + + String getName() { + return path.replaceAll(".h2.db", ""); + } + } + + public void stop() { + try { + dataSource.close(); + } catch (SQLException e) { + // Ignore error + } + } + + private void replaceSettings(Path path) { + settings + .setProperty(DatabaseProperties.PROP_DIALECT, DIALECT) + .setProperty(DatabaseProperties.PROP_DRIVER, DRIVER) + .setProperty(DatabaseProperties.PROP_USER, USER) + .setProperty(DatabaseProperties.PROP_PASSWORD, PASSWORD) + .setProperty(DatabaseProperties.PROP_URL, URL + path.getName()); + } + + private void configureDataSource(Path path) { + try { + dataSource = new BasicDataSource(); + dataSource.setDriverClassName(DRIVER); + dataSource.setUsername(USER); + dataSource.setPassword(PASSWORD); + dataSource.setUrl(URL + path.getName()); + } catch (Exception e) { + throw new SonarException("Fail to start local database", e); + } + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/LocalModeTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/LocalModeTest.java new file mode 100644 index 00000000000..e409d67c9e5 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/LocalModeTest.java @@ -0,0 +1,47 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.bootstrap; + +import org.junit.Test; +import org.sonar.api.config.Settings; + +import static org.fest.assertions.Assertions.assertThat; + +public class LocalModeTest { + Settings settings = new Settings(); + + @Test + public void should_be_disabled() { + LocalMode localMode = new LocalMode(settings); + localMode.start(); + + assertThat(localMode.isEnabled()).isFalse(); + } + + @Test + public void should_enable() { + settings.setProperty("sonar.local", "true"); + + LocalMode localMode = new LocalMode(settings); + localMode.start(); + + assertThat(localMode.isEnabled()).isTrue(); + } +} diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml index 6a6b4624d31..a3b6e9e349b 100644 --- a/sonar-server/pom.xml +++ b/sonar-server/pom.xml @@ -68,6 +68,11 @@ commons-configuration commons-configuration + + mysql + mysql-connector-java + 5.1.18 + com.h2database h2 diff --git a/sonar-server/src/main/java/org/sonar/server/database/LocalDatabaseFactory.java b/sonar-server/src/main/java/org/sonar/server/database/LocalDatabaseFactory.java new file mode 100644 index 00000000000..7a08c7e17ac --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/database/LocalDatabaseFactory.java @@ -0,0 +1,199 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.database; + +import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.SonarException; +import org.sonar.core.persistence.Database; +import org.sonar.core.persistence.DdlUtils; + +import javax.sql.DataSource; + +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class LocalDatabaseFactory implements ServerComponent { + private static final Logger LOG = LoggerFactory.getLogger(LocalDatabaseFactory.class); + + private static final String H2_DIALECT = "h2"; + private static final String H2_DRIVER = "org.h2.Driver"; + private static final String H2_URL = "jdbc:h2:"; + private static final String H2_USER = "sonar"; + private static final String H2_PASSWORD = "sonar"; + + private Database database; + + public LocalDatabaseFactory(Database database) { + this.database = database; + } + + public String createDatabaseForLocalMode() throws SQLException { + String name = "/tmp/" + System.nanoTime(); + + DataSource source = database.getDataSource(); + BasicDataSource destination = dataSource(H2_DRIVER, H2_USER, H2_PASSWORD, H2_URL + name); + + create(destination, H2_DIALECT); + + copyTable(source, destination, "PROPERTIES", "SELECT * FROM PROPERTIES WHERE (USER_ID IS NULL) AND (RESOURCE_ID IS NULL)"); + copyTable(source, destination, "RULES_PROFILES", "SELECT * FROM RULES_PROFILES"); + copyTable(source, destination, "RULES", "SELECT * FROM RULES"); + copyTable(source, destination, "RULES_PARAMETERS", "SELECT * FROM RULES_PARAMETERS"); + copyTable(source, destination, "ACTIVE_RULES", "SELECT * FROM ACTIVE_RULES"); + copyTable(source, destination, "ACTIVE_RULE_PARAMETERS", "SELECT * FROM ACTIVE_RULE_PARAMETERS"); + copyTable(source, destination, "METRICS", "SELECT * FROM METRICS"); + + destination.close(); + + return new File(name + ".h2.db").getAbsolutePath(); + } + + private void copyTable(DataSource source, DataSource dest, String table, String query) throws SQLException { + LOG.info("Copy table " + table); + + int colCount = getColumnCount(source, table); + + truncate(dest, table); + + Connection sourceConnection = null; + Statement sourceStatement = null; + ResultSet sourceResultSet = null; + Connection destConnection = null; + ResultSet destResultSet = null; + try { + sourceConnection = source.getConnection(); + sourceStatement = sourceConnection.createStatement(); + sourceResultSet = sourceStatement.executeQuery(query); + + destConnection = dest.getConnection(); + destConnection.setAutoCommit(false); + + PreparedStatement destStatement = destConnection.prepareStatement("INSERT INTO " + table + " VALUES(" + StringUtils.repeat("?", ",", colCount) + ")"); + while (sourceResultSet.next()) { + for (int col = 1; col <= colCount; col++) { + Object value = sourceResultSet.getObject(col); + destStatement.setObject(col, value); + } + destStatement.addBatch(); + } + + destStatement.executeBatch(); + destConnection.commit(); + destStatement.close(); + } finally { + closeQuietly(destResultSet); + closeQuietly(destConnection); + closeQuietly(sourceResultSet); + closeQuietly(sourceStatement); + closeQuietly(sourceConnection); + } + } + + private int getColumnCount(DataSource dataSource, String table) throws SQLException { + Connection connection = null; + ResultSet metaData = null; + try { + connection = dataSource.getConnection(); + metaData = connection.getMetaData().getColumns(null, null, table, null); + + int nbColumns = 0; + while (metaData.next()) { + nbColumns++; + } + + return nbColumns; + } finally { + closeQuietly(metaData); + closeQuietly(connection); + } + } + + private void truncate(DataSource dataSource, String table) throws SQLException { + Connection connection = null; + Statement statement = null; + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(); + statement.executeUpdate("TRUNCATE TABLE " + table); + } finally { + closeQuietly(statement); + closeQuietly(connection); + } + } + + private BasicDataSource dataSource(String driver, String user, String password, String url) { + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName(driver); + dataSource.setUsername(user); + dataSource.setPassword(password); + dataSource.setUrl(url); + return dataSource; + } + + public void create(DataSource dataSource, String dialect) throws SQLException { + Connection connection = null; + try { + connection = dataSource.getConnection(); + DdlUtils.createSchema(connection, dialect); + } catch (SQLException e) { + throw new SonarException("Fail to create local database schema", e); + } finally { + closeQuietly(connection); + } + } + + private void closeQuietly(Connection connection) { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // ignore + } + } + } + + private void closeQuietly(Statement statement) { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + // ignore + } + } + } + + private void closeQuietly(ResultSet resultSet) { + if (resultSet != null) { + try { + resultSet.close(); + } catch (SQLException e) { + // ignore + } + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 83ae16377ae..88b9ae05c03 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -47,7 +47,11 @@ import org.sonar.core.measure.MeasureFilterEngine; import org.sonar.core.measure.MeasureFilterExecutor; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; -import org.sonar.core.persistence.*; +import org.sonar.core.persistence.DaoUtils; +import org.sonar.core.persistence.DatabaseMigrator; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.DefaultDatabase; +import org.sonar.core.persistence.MyBatis; import org.sonar.core.qualitymodel.DefaultModelFinder; import org.sonar.core.rule.DefaultRuleFinder; import org.sonar.core.user.DefaultUserFinder; @@ -64,14 +68,35 @@ import org.sonar.server.charts.ChartFactory; import org.sonar.server.configuration.Backup; import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.database.EmbeddedDatabaseFactory; +import org.sonar.server.database.LocalDatabaseFactory; import org.sonar.server.notifications.NotificationService; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; -import org.sonar.server.plugins.*; +import org.sonar.server.plugins.ApplicationDeployer; +import org.sonar.server.plugins.DefaultServerPluginRepository; +import org.sonar.server.plugins.PluginDeployer; +import org.sonar.server.plugins.PluginDownloader; +import org.sonar.server.plugins.ServerExtensionInstaller; +import org.sonar.server.plugins.UpdateCenterClient; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.qualitymodel.DefaultModelManager; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; -import org.sonar.server.startup.*; -import org.sonar.server.ui.*; +import org.sonar.server.startup.DeleteDeprecatedMeasures; +import org.sonar.server.startup.GeneratePluginIndex; +import org.sonar.server.startup.GwtPublisher; +import org.sonar.server.startup.JdbcDriverDeployer; +import org.sonar.server.startup.RegisterMetrics; +import org.sonar.server.startup.RegisterNewDashboards; +import org.sonar.server.startup.RegisterNewFilters; +import org.sonar.server.startup.RegisterNewProfiles; +import org.sonar.server.startup.RegisterQualityModels; +import org.sonar.server.startup.RegisterRules; +import org.sonar.server.startup.ServerMetadataPersister; +import org.sonar.server.ui.CodeColorizers; +import org.sonar.server.ui.JRubyI18n; +import org.sonar.server.ui.PageDecorations; +import org.sonar.server.ui.SecurityRealmFactory; +import org.sonar.server.ui.Views; import javax.servlet.ServletContext; @@ -155,6 +180,7 @@ public final class Platform { rootContainer.addSingleton(I18nManager.class); rootContainer.addSingleton(RuleI18nManager.class); rootContainer.addSingleton(GwtI18n.class); + rootContainer.addSingleton(LocalDatabaseFactory.class); rootContainer.startComponents(); } @@ -223,6 +249,7 @@ public final class Platform { servicesContainer.addSingleton(MeasureFilterDecoder.class); servicesContainer.addSingleton(MeasureFilterExecutor.class); servicesContainer.addSingleton(MeasureFilterEngine.class); + servicesContainer.addSingleton(LocalDatabaseFactory.class); // Notifications servicesContainer.addSingleton(EmailSettings.class); diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb new file mode 100644 index 00000000000..d3cd927e6bf --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb @@ -0,0 +1,37 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2008-2012 SonarSource +# mailto:contact AT sonarsource DOT com +# +# Sonar 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. +# +# Sonar 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 Sonar; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 +# + +require "json" + +class Api::SynchroController < Api::ApiController + + # curl http://localhost:9000/api/synchro -v + def index + database_factory = java_facade.getCoreComponentByClassname('org.sonar.server.database.LocalDatabaseFactory') + + path = database_factory.createDatabaseForLocalMode() + + hash = {:path => path} + + respond_to do |format| + format.json { render :json => jsonp(hash) } + end + end +end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/active_rule.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/active_rule.rb index f2613920847..b7258fa7509 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/active_rule.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/active_rule.rb @@ -43,11 +43,11 @@ class ActiveRule < ActiveRecord::Base def warning? Sonar::RulePriority::minor?(failure_level) end - + def info? - Sonar::RulePriority::info?(failure_level) + Sonar::RulePriority::info?(failure_level) end - + def minor? Sonar::RulePriority::minor?(failure_level) end @@ -55,11 +55,11 @@ class ActiveRule < ActiveRecord::Base def major? Sonar::RulePriority::major?(failure_level) end - + def critical? Sonar::RulePriority::critical?(failure_level) end - + def blocker? Sonar::RulePriority::blocker?(failure_level) end @@ -98,8 +98,8 @@ class ActiveRule < ActiveRecord::Base new_active_rule = ActiveRule.new(:rule => rule, :failure_level => failure_level) self.active_rule_parameters.each do |active_rule_parameter| new_active_rule.active_rule_parameters << active_rule_parameter.copy - end - new_active_rule + end + new_active_rule end def inherited? diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb index a9eb887503f..57012bf4ae1 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb @@ -98,9 +98,9 @@ class Profile < ActiveRecord::Base def count_overriding_rules @count_overriding_rules||= - begin - active_rules.count(:conditions => ['inheritance=?', 'OVERRIDES']) - end + begin + active_rules.count(:conditions => ['inheritance=?', 'OVERRIDES']) + end end def inherited? @@ -109,13 +109,13 @@ class Profile < ActiveRecord::Base def parent @parent||= - begin - if parent_name.present? - Profile.find(:first, :conditions => ['language=? and name=?', language, parent_name]) - else - nil - end + begin + if parent_name.present? + Profile.find(:first, :conditions => ['language=? and name=?', language, parent_name]) + else + nil end + end end def children @@ -131,14 +131,14 @@ class Profile < ActiveRecord::Base def ancestors @ancestors ||= - begin - array=[] - if parent - array< 'LEFT JOIN properties ON properties.resource_id = projects.id', - :conditions => ['properties.resource_id is not null and properties.prop_key=? and properties.text_value like ?', "sonar.profile.#{language}", name]) - end + begin + Project.find(:all, + :joins => 'LEFT JOIN properties ON properties.resource_id = projects.id', + :conditions => ['properties.resource_id is not null and properties.prop_key=? and properties.text_value like ?', "sonar.profile.#{language}", name]) + end end def sorted_projects @@ -207,6 +207,15 @@ class Profile < ActiveRecord::Base @projects = nil end + def to_hash_json + { + :name => name, + :language => language, + :version => version, + :rules => active_rules.map { |active| active.rule.to_hash_json(self, active) } + } + end + def self.reset_default_profile_for_project_id(lang, project_id) Property.clear("sonar.profile.#{lang}", project_id) end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/rule.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/rule.rb index 8b55ca33938..bba01c56a70 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/rule.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/rule.rb @@ -94,11 +94,11 @@ class Rule < ActiveRecord::Base def description @l10n_description ||= - begin - result = Java::OrgSonarServerUi::JRubyFacade.getInstance().getRuleDescription(I18n.locale, repository_key, plugin_rule_key) - result = read_attribute(:description) unless result - result - end + begin + result = Java::OrgSonarServerUi::JRubyFacade.getInstance().getRuleDescription(I18n.locale, repository_key, plugin_rule_key) + result = read_attribute(:description) unless result + result + end end def description=(value) @@ -139,7 +139,7 @@ class Rule < ActiveRecord::Base def self.manual_rules rules = Rule.find(:all, :conditions => ['enabled=? and plugin_name=?', true, MANUAL_REPOSITORY_KEY]) - Api::Utils.insensitive_sort(rules) {|rule| rule.name} + Api::Utils.insensitive_sort(rules) { |rule| rule.name } end def self.manual_rule(id) @@ -154,7 +154,7 @@ class Rule < ActiveRecord::Base rule = Rule.find(:first, :conditions => {:enabled => true, :plugin_name => MANUAL_REPOSITORY_KEY, :plugin_rule_key => key}) if rule==nil && create_if_not_found description = options[:description] || Api::Utils.message('manual_rules.should_provide_real_description') - rule = Rule.create!(:enabled => true, :plugin_name => MANUAL_REPOSITORY_KEY, :plugin_rule_key => key, + rule = Rule.create!(:enabled => true, :plugin_name => MANUAL_REPOSITORY_KEY, :plugin_rule_key => key, :name => rule_id_or_name, :description => description) end end @@ -166,23 +166,22 @@ class Rule < ActiveRecord::Base checksum = nil level = Sonar::RulePriority.id(options['severity']||Severity::MAJOR) RuleFailure.create!( - :snapshot => resource.last_snapshot, - :rule => self, - :failure_level => level, - :message => options['message'], - :cost => (options['cost'] ? options['cost'].to_f : nil), - :switched_off => false, - :line => line, - :checksum => checksum) + :snapshot => resource.last_snapshot, + :rule => self, + :failure_level => level, + :message => options['message'], + :cost => (options['cost'] ? options['cost'].to_f : nil), + :switched_off => false, + :line => line, + :checksum => checksum) end - def to_hash_json(profile) + def to_hash_json(profile, active_rule = nil) json = {'title' => name, 'key' => key, 'plugin' => plugin_name, 'config_key' => config_key} json['description'] = description if description - active_rule = nil if profile - active_rule = profile.active_by_rule_id(id) + active_rule = active_rule || profile.active_by_rule_id(id) if active_rule json['priority'] = active_rule.priority_text json['status'] = 'ACTIVE' -- 2.39.5