aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server
diff options
context:
space:
mode:
authorDavid Gageot <david@gageot.net>2012-10-18 17:47:19 +0200
committerDavid Gageot <david@gageot.net>2012-10-23 11:48:21 +0200
commita505a98b4d1ad33c80b939ae6fed12043e635ded (patch)
tree939b5de8fce6a3f5e3ad150c3ca814c429d462b6 /sonar-server
parent9f509a9600f0dafb62d8c9787d94a910762de385 (diff)
downloadsonarqube-a505a98b4d1ad33c80b939ae6fed12043e635ded.tar.gz
sonarqube-a505a98b4d1ad33c80b939ae6fed12043e635ded.zip
SONAR-3895 Local mode
Diffstat (limited to 'sonar-server')
-rw-r--r--sonar-server/pom.xml5
-rw-r--r--sonar-server/src/main/java/org/sonar/server/database/LocalDatabaseFactory.java199
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/Platform.java35
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb37
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/active_rule.rb14
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb51
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/rule.rb35
7 files changed, 326 insertions, 50 deletions
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
@@ -69,6 +69,11 @@
<artifactId>commons-configuration</artifactId>
</dependency>
<dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <version>5.1.18</version>
+ </dependency>
+ <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
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<<parent
- array.concat(parent.ancestors)
- end
- array
+ begin
+ array=[]
+ if parent
+ array<<parent
+ array.concat(parent.ancestors)
end
+ array
+ end
end
def import_configuration(importer_key, file)
@@ -186,11 +186,11 @@ class Profile < ActiveRecord::Base
def projects
@projects ||=
- 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
+ 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'