]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3895 Local mode
authorDavid Gageot <david@gageot.net>
Thu, 18 Oct 2012 15:47:19 +0000 (17:47 +0200)
committerDavid Gageot <david@gageot.net>
Tue, 23 Oct 2012 09:48:21 +0000 (11:48 +0200)
12 files changed:
sonar-batch/pom.xml
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/LocalMode.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/LocalModeTest.java [new file with mode: 0644]
sonar-server/pom.xml
sonar-server/src/main/java/org/sonar/server/database/LocalDatabaseFactory.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/active_rule.rb
sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb
sonar-server/src/main/webapp/WEB-INF/app/models/rule.rb

index 7255d9719b25f046b634faeae8666a7fd6bfcec3..6a1a222b901e79525199b0e7b47f3b0fc8f845a3 100644 (file)
       <groupId>commons-lang</groupId>
       <artifactId>commons-lang</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>2.2.2</version>
+    </dependency>
 
     <!-- unit tests -->
     <dependency>
index 528186df9f4c9acfd7516cf255846ca34e1ffd7b..f5bfb3eadd4c906de9891e29dfda0e85c18c974f 100644 (file)
@@ -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 (file)
index 0000000..618a1e7
--- /dev/null
@@ -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 (file)
index 0000000..a8e1877
--- /dev/null
@@ -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 (file)
index 0000000..e409d67
--- /dev/null
@@ -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();
+  }
+}
index 6a6b4624d31067715db2a9c51a70a8444c1b617b..a3b6e9e349b8916ab509e12175378dff3a3d7e94 100644 (file)
       <groupId>commons-configuration</groupId>
       <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>
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 (file)
index 0000000..7a08c7e
--- /dev/null
@@ -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
+      }
+    }
+  }
+}
index 83ae16377ae58bd9293aae79f40ab1b9dac131e4..88b9ae05c0347bdf4299e14157ea3908e149e2bd 100644 (file)
@@ -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 (file)
index 0000000..d3cd927
--- /dev/null
@@ -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
index f2613920847558fd184668475aacf89d0ed694cb..b7258fa750991524420467bb831f86ec76719e30 100644 (file)
@@ -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?
index a9eb887503ff41cacb1d0f75946fa12c07fb3aaa..57012bf4ae12ad903dde877fc77ea8b8962749c6 100644 (file)
@@ -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
index 8b55ca33938504d6c5d0c6a5c4f33574638d1916..bba01c56a7055b64d12e40d1e70bd45df9042e1b 100644 (file)
@@ -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'