]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3895 load settings from web service
authorSimon Brandhof <simon.brandhof@gmail.com>
Mon, 5 Nov 2012 13:22:11 +0000 (14:22 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Mon, 5 Nov 2012 13:23:47 +0000 (14:23 +0100)
34 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/ApplyProjectRolesDecorator.java
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/DatabaseBatchCompatibility.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/DatabaseCompatibility.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExclusions.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectModule.java
sonar-batch/src/main/java/org/sonar/batch/config/BatchDatabaseSettingsLoader.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/config/BootstrapSettings.java
sonar-batch/src/main/java/org/sonar/batch/config/BootstrapSettingsLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/config/ProjectSettings.java
sonar-batch/src/main/java/org/sonar/batch/local/DryRunDatabase.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/DatabaseBatchCompatibilityTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/DatabaseCompatibilityTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectModuleTest.java
sonar-batch/src/test/java/org/sonar/batch/config/ProjectSettingsTest.java
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
sonar-core/src/main/java/org/sonar/core/persistence/DbTemplate.java
sonar-core/src/main/java/org/sonar/core/persistence/DefaultDatabase.java
sonar-core/src/main/java/org/sonar/core/persistence/DryRunDatabaseFactory.java
sonar-core/src/test/java/org/sonar/core/persistence/DatabaseVersionTest.java
sonar-core/src/test/java/org/sonar/core/persistence/DryRunDatabaseFactoryTest.java
sonar-core/src/test/resources/org/sonar/core/persistence/DatabaseVersionTest/getSonarCoreId.xml [deleted file]
sonar-core/src/test/resources/org/sonar/core/persistence/DryRunDatabaseFactoryTest/should_create_database.xml
sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/properties_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/models/project.rb
sonar-ws-client/src/test/java/org/sonar/wsclient/services/PropertyQueryTest.java

index fc5e21bc95f2a324b565454226e1bbb46cbed093..d2a4e7b3f81b431c1d4e54e5b58a15b43faa2b3d 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DryRunIncompatible;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
@@ -30,6 +31,7 @@ import org.sonar.api.security.ResourcePermissions;
 
 import java.util.Set;
 
+@DryRunIncompatible
 public class ApplyProjectRolesDecorator implements Decorator {
 
   private final ResourcePermissions resourcePermissions;
index d911beb4399e5bd0860b095aaa10271825e48ce4..81ac5d09f36e46e7bc3e6a75017a0381ccf77258 100644 (file)
 package org.sonar.plugins.cpd;
 
 import com.google.common.collect.ImmutableList;
-import org.sonar.api.*;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.Properties;
+import org.sonar.api.Property;
+import org.sonar.api.PropertyType;
+import org.sonar.api.SonarPlugin;
 import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator;
 import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator;
 import org.sonar.plugins.cpd.index.IndexFactory;
@@ -37,6 +41,15 @@ import java.util.List;
     module = true,
     global = true,
     category = CoreProperties.CATEGORY_DUPLICATIONS,
+    type = PropertyType.BOOLEAN),
+  @Property(
+    key = CoreProperties.CPD_SKIP_PROPERTY,
+    defaultValue = "false",
+    name = "Skip",
+    description = "Disable detection of duplications",
+    // not displayed in UI
+    project = false, module = false, global = false,
+    category = CoreProperties.CATEGORY_DUPLICATIONS,
     type = PropertyType.BOOLEAN)
 })
 public final class CpdPlugin extends SonarPlugin {
index 754ef176ee02130c3184728f5ada7ee1184d6f87..bd3f6420710407d8249f9c1e94f26e6385e04d86 100644 (file)
@@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.Sensor;
 import org.sonar.api.batch.SensorContext;
+import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
 
 public class CpdSensor implements Sensor {
@@ -34,10 +35,12 @@ public class CpdSensor implements Sensor {
 
   private CpdEngine sonarEngine;
   private CpdEngine sonarBridgeEngine;
+  private Settings settings;
 
-  public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine) {
+  public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine, Settings settings) {
     this.sonarEngine = sonarEngine;
     this.sonarBridgeEngine = sonarBridgeEngine;
+    this.settings = settings;
   }
 
   public boolean shouldExecuteOnProject(Project project) {
@@ -65,9 +68,11 @@ public class CpdSensor implements Sensor {
 
   @VisibleForTesting
   boolean isSkipped(Project project) {
-    Configuration conf = project.getConfiguration();
-    return conf.getBoolean("sonar.cpd." + project.getLanguageKey() + ".skip",
-        conf.getBoolean(CoreProperties.CPD_SKIP_PROPERTY, false));
+    String key = "sonar.cpd." + project.getLanguageKey() + ".skip";
+    if (settings.hasKey(key)) {
+      return settings.getBoolean(key);
+    }
+    return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY);
   }
 
   public void analyse(Project project, SensorContext context) {
index 033e359ec003a831f23269537de83bbabb0e3805..21335c46d0af5a9942bacab32a8f36bd4cf50ff5 100644 (file)
@@ -38,15 +38,6 @@ public class IndexFactory implements BatchExtension {
   private final ResourcePersister resourcePersister;
   private final DuplicationDao dao;
 
-  /**
-   * For dry run, where is no access to database.
-   */
-  public IndexFactory(Settings settings) {
-    this.settings = settings;
-    this.resourcePersister = null;
-    this.dao = null;
-  }
-
   public IndexFactory(Settings settings, ResourcePersister resourcePersister, DuplicationDao dao) {
     this.settings = settings;
     this.resourcePersister = resourcePersister;
@@ -54,23 +45,28 @@ public class IndexFactory implements BatchExtension {
   }
 
   public SonarDuplicationsIndex create(Project project) {
-    if (isCrossProject(project)) {
-      LOG.info("Cross-project analysis enabled");
+    if (verifyCrossProject(project, LOG)) {
       return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao));
-    } else {
-      LOG.info("Cross-project analysis disabled");
-      return new SonarDuplicationsIndex();
     }
+    return new SonarDuplicationsIndex();
   }
 
-  /**
-   * @return true, if was enabled by user and database is available
-   */
   @VisibleForTesting
-  boolean isCrossProject(Project project) {
-    return settings.getBoolean(CoreProperties.CPD_CROSS_RPOJECT)
-      && resourcePersister != null && dao != null
-      && StringUtils.isBlank(project.getBranch());
-  }
+  boolean verifyCrossProject(Project project, Logger logger) {
+    boolean crossProject = false;
 
+    if (settings.getBoolean(CoreProperties.CPD_CROSS_RPOJECT)) {
+      if (settings.getBoolean("sonar.dryRun")) {
+        logger.info("Cross-project analysis disabled. Not supported on dry runs.");
+      } else if (StringUtils.isNotBlank(project.getBranch())) {
+        logger.info("Cross-project analysis disabled. Not supported on project branches.");
+      } else {
+        logger.info("Cross-project analysis enabled");
+        crossProject = true;
+      }
+    } else {
+      logger.info("Cross-project analysis disabled");
+    }
+    return crossProject;
+  }
 }
index 0c309396e55b4db3b0c8accfb84eb25feaa7a84b..a3ec3f050485c0723255ae32c20079c2e9486dae 100644 (file)
  */
 package org.sonar.plugins.cpd;
 
-import static org.hamcrest.number.OrderingComparisons.greaterThan;
-import static org.junit.Assert.assertThat;
 import org.junit.Test;
 
+import static org.fest.assertions.Assertions.assertThat;
+
 public class CpdPluginTest {
 
   @Test
   public void getExtensions() {
-    assertThat(new CpdPlugin().getExtensions().size(), greaterThan(1));
+    assertThat(new CpdPlugin().getExtensions()).hasSize(6);
   }
 }
index d49dd75daf213ce5d54c09c0091cb012e18cc282..4d17611c5f04fa41095c15117b65921d3d9b20ed 100644 (file)
@@ -22,70 +22,55 @@ package org.sonar.plugins.cpd;
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Java;
 import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Project;
 import org.sonar.plugins.cpd.index.IndexFactory;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 public class CpdSensorTest {
 
-  private SonarEngine sonarEngine;
-  private SonarBridgeEngine sonarBridgeEngine;
-  private CpdSensor sensor;
+  SonarEngine sonarEngine;
+  SonarBridgeEngine sonarBridgeEngine;
+  CpdSensor sensor;
+  Settings settings;
 
   @Before
   public void setUp() {
-    IndexFactory indexFactory = new IndexFactory(null);
+    IndexFactory indexFactory = mock(IndexFactory.class);
     sonarEngine = new SonarEngine(indexFactory);
     sonarBridgeEngine = new SonarBridgeEngine(indexFactory);
-    sensor = new CpdSensor(sonarEngine, sonarBridgeEngine);
+    settings = new Settings(new PropertyDefinitions(CpdPlugin.class));
+    sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings);
   }
 
   @Test
-  public void generalSkip() {
-    PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty("sonar.cpd.skip", "true");
-
-    Project project = createJavaProject().setConfiguration(conf);
-
-    assertTrue(sensor.isSkipped(project));
+  public void test_global_skip() {
+    settings.setProperty("sonar.cpd.skip", true);
+    assertThat(sensor.isSkipped(createJavaProject())).isTrue();
   }
 
   @Test
-  public void doNotSkipByDefault() {
-    Project project = createJavaProject().setConfiguration(new PropertiesConfiguration());
-
-    assertFalse(sensor.isSkipped(project));
+  public void should_not_skip_by_default() {
+    assertThat(sensor.isSkipped(createJavaProject())).isFalse();
   }
 
   @Test
-  public void shouldSkipByLanguage() {
-
-    Project phpProject = createPhpProject();
-    phpProject.getConfiguration().setProperty("sonar.cpd.skip", "false");
-    phpProject.getConfiguration().setProperty("sonar.cpd.php.skip", "true");
-    assertTrue(sensor.isSkipped(phpProject));
-
-    Project javaProject = createJavaProject();
-    javaProject.getConfiguration().setProperty("sonar.cpd.skip", "false");
-    javaProject.getConfiguration().setProperty("sonar.cpd.php.skip", "true");
-    assertFalse(sensor.isSkipped(javaProject));
-
+  public void should_skip_by_language() {
+    settings.setProperty("sonar.cpd.skip", false);
+    settings.setProperty("sonar.cpd.php.skip", true);
+    assertThat(sensor.isSkipped(createPhpProject())).isTrue();
+    assertThat(sensor.isSkipped(createJavaProject())).isFalse();
   }
 
   @Test
-  public void engine() {
-    Project phpProject = createPhpProject();
-    Project javaProject = createJavaProject();
-
-    assertThat(sensor.getEngine(javaProject), is((CpdEngine) sonarEngine));
-    assertThat(sensor.getEngine(phpProject), is((CpdEngine) sonarBridgeEngine));
+  public void test_engine() {
+    assertThat(sensor.getEngine(createJavaProject())).isSameAs(sonarEngine);
+    assertThat(sensor.getEngine(createPhpProject())).isSameAs(sonarBridgeEngine);
   }
 
   private Project createJavaProject() {
index 840a08a1af9a001cdea709edf1f4f7917c30484b..e73bffa4acc6fa075609266daae1f00a4f91b395 100644 (file)
@@ -21,54 +21,57 @@ package org.sonar.plugins.cpd.index;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.slf4j.Logger;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
 import org.sonar.batch.index.ResourcePersister;
 import org.sonar.core.duplication.DuplicationDao;
 
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 public class IndexFactoryTest {
 
-  private Project project;
-  private Settings settings;
+  Project project;
+  Settings settings;
+  IndexFactory factory;
+  Logger logger;
 
   @Before
   public void setUp() {
     project = new Project("foo");
     settings = new Settings();
+    factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
+    logger = mock(Logger.class);
   }
 
   @Test
   public void crossProjectEnabled() {
     settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
-    IndexFactory factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
-    assertThat(factory.isCrossProject(project), is(true));
+    assertThat(factory.verifyCrossProject(project, logger)).isTrue();
   }
 
   @Test
   public void noCrossProjectWithBranch() {
     settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
-    IndexFactory factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
     project.setBranch("branch");
-    assertThat(factory.isCrossProject(project), is(false));
+    assertThat(factory.verifyCrossProject(project, logger)).isFalse();
   }
 
   @Test
-  public void noCrossProjectWithoutDatabase() {
+  public void cross_project_should_be_disabled_on_dry_run() {
     settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
-    IndexFactory factory = new IndexFactory(settings);
-    assertThat(factory.isCrossProject(project), is(false));
+    settings.setProperty("sonar.dryRun", "true");
+    assertThat(factory.verifyCrossProject(project, logger)).isFalse();
+    verify(logger).info("Cross-project analysis disabled. Not supported on dry runs.");
   }
 
   @Test
   public void crossProjectDisabled() {
     settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "false");
-    IndexFactory factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
-    assertThat(factory.isCrossProject(project), is(false));
+    assertThat(factory.verifyCrossProject(project, logger)).isFalse();
   }
 
 }
index 590d503bcf15bfc9624558265d0647fff267b438..0414d6ee42427f770591d037fdb7a2917ed6ff93 100644 (file)
@@ -34,7 +34,6 @@ import org.sonar.batch.components.PastSnapshotFinderByDays;
 import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis;
 import org.sonar.batch.components.PastSnapshotFinderByPreviousVersion;
 import org.sonar.batch.components.PastSnapshotFinderByVersion;
-import org.sonar.batch.config.BatchDatabaseSettingsLoader;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.DefaultPersistenceManager;
 import org.sonar.batch.index.DefaultResourcePersister;
@@ -111,7 +110,7 @@ public class BatchModule extends Module {
     container.addSingleton(BatchDatabase.class);
     container.addSingleton(MyBatis.class);
     container.addSingleton(DatabaseVersion.class);
-    container.addSingleton(DatabaseBatchCompatibility.class);
+    container.addSingleton(DatabaseCompatibility.class);
     for (Class daoClass : DaoUtils.getDaoClasses()) {
       container.addSingleton(daoClass);
     }
@@ -120,7 +119,6 @@ public class BatchModule extends Module {
     container.addSingleton(DefaultDatabaseConnector.class);
     container.addSingleton(JpaDatabaseSession.class);
     container.addSingleton(BatchDatabaseSessionFactory.class);
-    container.addSingleton(BatchDatabaseSettingsLoader.class);
   }
 
   private void registerBatchExtensions() {
index bff8f081095b6edfa20a326c2553ce1c35a6db4e..6951993e578ea71dddefdc79956cca50349b611b 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.batch.FakeMavenPluginExecutor;
 import org.sonar.batch.MavenPluginExecutor;
 import org.sonar.batch.ServerMetadata;
 import org.sonar.batch.config.BootstrapSettings;
+import org.sonar.batch.config.BootstrapSettingsLoader;
 import org.sonar.core.config.Logback;
 import org.sonar.wsclient.Sonar;
 
@@ -60,6 +61,7 @@ public class BootstrapModule extends Module {
     container.addSingleton(HttpDownloader.class);
     container.addSingleton(UriReader.class);
     container.addSingleton(PluginDownloader.class);
+    container.addSingleton(BootstrapSettingsLoader.class);
     for (Object component : boostrapperComponents) {
       if (component != null) {
         container.addSingleton(component);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DatabaseBatchCompatibility.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DatabaseBatchCompatibility.java
deleted file mode 100644 (file)
index 9017d32..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.sonar.api.BatchComponent;
-import org.sonar.api.config.Settings;
-import org.sonar.api.database.DatabaseProperties;
-import org.sonar.core.persistence.BadDatabaseVersion;
-import org.sonar.core.persistence.DatabaseVersion;
-
-/**
- * Detects if database is not up-to-date with the version required by the batch.
- */
-public class DatabaseBatchCompatibility implements BatchComponent {
-
-  private DatabaseVersion version;
-  private Settings settings;
-  private ServerClient server;
-
-  public DatabaseBatchCompatibility(DatabaseVersion version, ServerClient server, Settings settings) {
-    this.version = version;
-    this.server = server;
-    this.settings = settings;
-  }
-
-  public void start() {
-    checkCorrectServerId();
-    checkDatabaseStatus();
-  }
-
-  private void checkCorrectServerId() {
-    if (!version.getSonarCoreId().equals(server.getServerId())) {
-      StringBuilder message = new StringBuilder("The current batch process and the configured remote server do not share the same DB configuration.\n");
-      message.append("\t- Batch side: ");
-      message.append(settings.getString(DatabaseProperties.PROP_URL));
-      message.append(" (");
-      String userName = settings.getString(DatabaseProperties.PROP_USER);
-      message.append(userName == null ? "sonar" : userName);
-      message.append(" / *****)\n\t- Server side: check the configuration at ");
-      message.append(server.getURL());
-      message.append("/system\n");
-      throw new BadDatabaseVersion(message.toString());
-    }
-  }
-
-  private void checkDatabaseStatus() {
-    DatabaseVersion.Status status = version.getStatus();
-    if (status == DatabaseVersion.Status.REQUIRES_DOWNGRADE) {
-      throw new BadDatabaseVersion("Database relates to a more recent version of Sonar. Please check your settings (JDBC settings, version of Maven plugin)");
-    }
-    if (status == DatabaseVersion.Status.REQUIRES_UPGRADE) {
-      throw new BadDatabaseVersion("Database must be upgraded. Please browse " + server.getURL() + "/setup");
-    }
-    if (status != DatabaseVersion.Status.UP_TO_DATE) {
-      // Support other future values
-      throw new BadDatabaseVersion("Unknown database status: " + status);
-    }
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DatabaseCompatibility.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DatabaseCompatibility.java
new file mode 100644 (file)
index 0000000..3d1c06e
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.sonar.api.BatchComponent;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.database.DatabaseProperties;
+import org.sonar.core.persistence.BadDatabaseVersion;
+import org.sonar.core.persistence.DatabaseVersion;
+
+/**
+ * Detects if database is not up-to-date with the version required by the batch.
+ */
+public class DatabaseCompatibility implements BatchComponent {
+
+  private DatabaseVersion version;
+  private Settings settings;
+  private ServerClient server;
+
+  public DatabaseCompatibility(DatabaseVersion version, ServerClient server, Settings settings) {
+    this.version = version;
+    this.server = server;
+    this.settings = settings;
+  }
+
+  public void start() {
+    if (!settings.getBoolean("sonar.dryRun")) {
+      checkCorrectServerId();
+      checkDatabaseStatus();
+    }
+  }
+
+  private void checkCorrectServerId() {
+    if (!settings.getString(CoreProperties.SERVER_ID).equals(server.getServerId())) {
+      StringBuilder message = new StringBuilder("The current batch process and the configured remote server do not share the same DB configuration.\n");
+      message.append("\t- Batch side: ");
+      message.append(settings.getString(DatabaseProperties.PROP_URL));
+      message.append(" (");
+      String userName = settings.getString(DatabaseProperties.PROP_USER);
+      message.append(userName == null ? "sonar" : userName);
+      message.append(" / *****)\n\t- Server side: check the configuration at ");
+      message.append(server.getURL());
+      message.append("/system\n");
+      throw new BadDatabaseVersion(message.toString());
+    }
+  }
+
+  private void checkDatabaseStatus() {
+    DatabaseVersion.Status status = version.getStatus();
+    if (status == DatabaseVersion.Status.REQUIRES_DOWNGRADE) {
+      throw new BadDatabaseVersion("Database relates to a more recent version of Sonar. Please check your settings (JDBC settings, version of Maven plugin)");
+    }
+    if (status == DatabaseVersion.Status.REQUIRES_UPGRADE) {
+      throw new BadDatabaseVersion("Database must be upgraded. Please browse " + server.getURL() + "/setup");
+    }
+    if (status != DatabaseVersion.Status.UP_TO_DATE) {
+      // Support other future values
+      throw new BadDatabaseVersion("Unknown database status: " + status);
+    }
+  }
+
+}
index 050dae891544974f80068ca5113f91a755663f34..9b078d2f249c511a58cbbe0867eaf76de86034a7 100644 (file)
@@ -42,7 +42,9 @@ public class ProjectExclusions implements BatchComponent {
   private Settings settings;
   private ProjectReactor reactor;
 
-  public ProjectExclusions(Settings settings, ProjectReactor reactor, ProjectBuilder[] projectBuilders) {
+  public ProjectExclusions(Settings settings, ProjectReactor reactor,
+                           // exclusions are applied when the project is completely defined by extensions
+                           ProjectBuilder[] projectBuilders) {
     this.settings = settings;
     this.reactor = reactor;
   }
index 4d3450b6247c5cd33180924b9d314a1d5e1e4277..0acaa7c9ba0c90e0233c42f0ce05b371b65b3e3a 100644 (file)
@@ -73,8 +73,8 @@ public class ProjectModule extends Module {
     }
     ProjectDefinition projectDefinition = container.getComponentByType(ProjectTree.class).getProjectDefinition(project);
     container.addSingleton(projectDefinition);
-    container.addSingleton(project);
     container.addSingleton(project.getConfiguration());
+    container.addSingleton(project);
     container.addSingleton(ProjectSettings.class);
     container.addSingleton(UnsupportedProperties.class);
 
diff --git a/sonar-batch/src/main/java/org/sonar/batch/config/BatchDatabaseSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/config/BatchDatabaseSettingsLoader.java
deleted file mode 100644 (file)
index 8a03eb0..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.config;
-
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.core.properties.PropertiesDao;
-import org.sonar.core.properties.PropertyDto;
-
-import java.util.List;
-
-/**
- * @since 2.12
- */
-public final class BatchDatabaseSettingsLoader {
-
-  private PropertiesDao propertiesDao;
-  private BootstrapSettings settings;
-  private ProjectReactor reactor;
-
-  public BatchDatabaseSettingsLoader(PropertiesDao propertiesDao, BootstrapSettings settings, ProjectReactor reactor) {
-    this.propertiesDao = propertiesDao;
-    this.settings = settings;
-    this.reactor = reactor;
-  }
-
-  public void start() {
-    String branch = settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
-    String projectKey = reactor.getRoot().getKey();
-    if (StringUtils.isNotBlank(branch)) {
-      projectKey = String.format("%s:%s", projectKey, branch);
-    }
-    setIfNotDefined(propertiesDao.selectProjectProperties(projectKey));
-    setIfNotDefined(propertiesDao.selectGlobalProperties());
-    settings.updateDeprecatedCommonsConfiguration();
-  }
-
-  private void setIfNotDefined(List<PropertyDto> dbProperties) {
-    for (PropertyDto dbProperty : dbProperties) {
-      if (!settings.hasKey(dbProperty.getKey())) {
-        settings.setProperty(dbProperty.getKey(), dbProperty.getValue());
-      }
-    }
-  }
-}
index a2267e87cc7bc098c2ef855e7c06c23361c08e00..1d7caab9509fc824bf03cdfd28fa6fbedd446fa6 100644 (file)
@@ -28,7 +28,7 @@ import org.sonar.core.config.ConfigurationUtils;
 /**
  * @since 2.12
  */
-public final class BootstrapSettings extends Settings {
+public class BootstrapSettings extends Settings {
   private Configuration deprecatedConfiguration;
   private ProjectReactor reactor;
 
diff --git a/sonar-batch/src/main/java/org/sonar/batch/config/BootstrapSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/config/BootstrapSettingsLoader.java
new file mode 100644 (file)
index 0000000..02563c7
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.config;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.wsclient.Sonar;
+import org.sonar.wsclient.services.Property;
+import org.sonar.wsclient.services.PropertyQuery;
+
+import java.util.List;
+
+/**
+ * Load global settings and project settings. Note that the definition of modules is
+ * incomplete before the execution of ProjectBuilder extensions, so module settings
+ * are not loaded yet.
+ * @since 3.4
+ */
+public final class BootstrapSettingsLoader {
+
+  private BootstrapSettings settings;
+  private ProjectReactor reactor;
+  private Sonar wsClient;
+
+  public BootstrapSettingsLoader(BootstrapSettings settings, ProjectReactor reactor, Sonar wsClient) {
+    this.settings = settings;
+    this.reactor = reactor;
+    this.wsClient = wsClient;
+  }
+
+  public void start() {
+    LoggerFactory.getLogger(BootstrapSettingsLoader.class).info("Load project settings");
+    String branch = settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
+    String projectKey = reactor.getRoot().getKey();
+    if (StringUtils.isNotBlank(branch)) {
+      projectKey = String.format("%s:%s", projectKey, branch);
+    }
+    List<Property> wsProperties = wsClient.findAll(PropertyQuery.createForAll().setResourceKeyOrId(projectKey));
+    for (Property wsProperty : wsProperties) {
+      setIfNotDefined(wsProperty);
+    }
+    settings.updateDeprecatedCommonsConfiguration();
+  }
+
+  private void setIfNotDefined(Property wsProperty) {
+    if (!settings.hasKey(wsProperty.getKey())) {
+      settings.setProperty(wsProperty.getKey(), wsProperty.getValue());
+    }
+  }
+}
index d470a9cdb02135dd0777f836dd69440891067d08..330100f328bcf46dfb170a87bd3c68c9dc43f9dc 100644 (file)
@@ -22,14 +22,16 @@ package org.sonar.batch.config;
 import com.google.common.collect.Lists;
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
 import org.sonar.core.config.ConfigurationUtils;
-import org.sonar.core.properties.PropertiesDao;
-import org.sonar.core.properties.PropertyDto;
+import org.sonar.wsclient.Sonar;
+import org.sonar.wsclient.services.Property;
+import org.sonar.wsclient.services.PropertyQuery;
+
+import javax.annotation.Nullable;
 
 import java.util.List;
 
@@ -39,81 +41,77 @@ import java.util.List;
 public class ProjectSettings extends Settings {
 
   private Configuration deprecatedCommonsConf;
-  private ProjectDefinition projectDefinition;
-  private PropertiesDao propertiesDao;
 
-  public ProjectSettings(PropertyDefinitions definitions, ProjectDefinition projectDefinition, PropertiesDao propertiesDao, Project project) {
-    super(definitions);
-    this.deprecatedCommonsConf = project.getConfiguration(); // Configuration is not a parameter to be sure that the project conf is used, not the global one
-    this.projectDefinition = projectDefinition;
-    this.propertiesDao = propertiesDao;
-    load();
-  }
+  public ProjectSettings(BootstrapSettings bootstrapSettings, ProjectDefinition project,
+                         Sonar wsClient, Configuration deprecatedCommonsConf) {
+    super(bootstrapSettings.getDefinitions());
 
-  public ProjectSettings load() {
-    clear();
+    LoggerFactory.getLogger(ProjectSettings.class).info("Load module settings");
+    this.deprecatedCommonsConf = deprecatedCommonsConf;
+    if (project.getParent() == null) {
+      // root project -> no need to reload settings
+      copy(bootstrapSettings);
+    } else {
+      init(project, bootstrapSettings, wsClient);
+    }
+  }
 
-    // hack to obtain "sonar.branch" before loading settings from database
-    loadBuildProperties();
-    addEnvironmentVariables();
-    addSystemProperties();
-    String branch = getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
-    clear();
+  private void copy(BootstrapSettings bootstrapSettings) {
+    setProperties(bootstrapSettings);
+  }
 
-    // order is important -> bottom-up. The last one overrides all the others.
-    loadDatabaseGlobalSettings();
-    loadDatabaseProjectSettings(projectDefinition, branch);
-    loadBuildProperties();
+  private ProjectSettings init(ProjectDefinition project, BootstrapSettings bootstrapSettings, Sonar wsClient) {
+    addPersistedProperties(project, bootstrapSettings, wsClient);
+    addBuildProperties(project);
     addEnvironmentVariables();
     addSystemProperties();
-
-    updateDeprecatedCommonsConfiguration();
-
     return this;
   }
 
-  private void loadBuildProperties() {
-    List<ProjectDefinition> orderedProjects = getOrderedProjects(projectDefinition);
-    for (ProjectDefinition p : orderedProjects) {
-      addProperties(p.getProperties());
-    }
-  }
-
-  private void loadDatabaseProjectSettings(ProjectDefinition projectDef, String branch) {
-    if (projectDef.getParent() != null) {
-      loadDatabaseProjectSettings(projectDef.getParent(), branch);
-    }
-    String projectKey = projectDef.getKey();
+  private void addPersistedProperties(ProjectDefinition project, BootstrapSettings bootstrapSettings, Sonar wsClient) {
+    String branch = bootstrapSettings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
+    String projectKey = project.getKey();
     if (StringUtils.isNotBlank(branch)) {
       projectKey = String.format("%s:%s", projectKey, branch);
     }
-    List<PropertyDto> props = propertiesDao.selectProjectProperties(projectKey);
-    for (PropertyDto dbProperty : props) {
-      setProperty(dbProperty.getKey(), dbProperty.getValue());
+    List<Property> wsProperties = wsClient.findAll(PropertyQuery.createForAll().setResourceKeyOrId(projectKey));
+    for (Property wsProperty : wsProperties) {
+      setProperty(wsProperty.getKey(), wsProperty.getValue());
     }
   }
 
-  private void loadDatabaseGlobalSettings() {
-    List<PropertyDto> props = propertiesDao.selectGlobalProperties();
-    for (PropertyDto dbProperty : props) {
-      setProperty(dbProperty.getKey(), dbProperty.getValue());
+  private void addBuildProperties(ProjectDefinition project) {
+    List<ProjectDefinition> orderedProjects = getTopDownParentProjects(project);
+    for (ProjectDefinition p : orderedProjects) {
+      addProperties(p.getProperties());
     }
   }
 
-  private void updateDeprecatedCommonsConfiguration() {
-    ConfigurationUtils.copyToCommonsConfiguration(properties, deprecatedCommonsConf);
-  }
-
   /**
-   * From root to module
+   * From root to given project
    */
-  static List<ProjectDefinition> getOrderedProjects(ProjectDefinition project) {
+  static List<ProjectDefinition> getTopDownParentProjects(ProjectDefinition project) {
     List<ProjectDefinition> result = Lists.newArrayList();
-    ProjectDefinition pd = project;
-    while (pd != null) {
-      result.add(0, pd);
-      pd = pd.getParent();
+    ProjectDefinition p = project;
+    while (p != null) {
+      result.add(0, p);
+      p = p.getParent();
     }
     return result;
   }
+
+  @Override
+  protected void doOnSetProperty(String key, @Nullable String value) {
+    deprecatedCommonsConf.setProperty(key, value);
+  }
+
+  @Override
+  protected void doOnRemoveProperty(String key) {
+    deprecatedCommonsConf.clearProperty(key);
+  }
+
+  @Override
+  protected void doOnClearProperties() {
+    deprecatedCommonsConf.clear();
+  }
 }
index c2f69706d582060dfe06da3deac2225cf35be520..4149940feb84840b1dea753748c7bd8658699eda 100644 (file)
@@ -71,6 +71,7 @@ public class DryRunDatabase implements BatchComponent {
       return;
     }
 
+    LOG.info("Install dry run database");
     File databaseFile = tempDirectories.getFile("dry_run", "db.h2.db");
     downloadDatabase(reactor.getRoot().getKey(), databaseFile);
 
@@ -79,8 +80,6 @@ public class DryRunDatabase implements BatchComponent {
   }
 
   private void downloadDatabase(String projectKey, File toFile) {
-    LOG.info("Downloading DryRun database for project [{}]", projectKey);
-
     try {
       server.download(API_SYNCHRO + "?resource=" + projectKey, toFile);
     } catch (SonarException e) {
@@ -95,8 +94,6 @@ public class DryRunDatabase implements BatchComponent {
   }
 
   private void replaceSettings(String databasePath) {
-    LOG.info("Overriding database settings");
-
     settings
         .setProperty("sonar.jdbc.schema", "")
         .setProperty(DatabaseProperties.PROP_DIALECT, DIALECT)
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DatabaseBatchCompatibilityTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DatabaseBatchCompatibilityTest.java
deleted file mode 100644 (file)
index bddab1a..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.Settings;
-import org.sonar.api.database.DatabaseProperties;
-import org.sonar.api.utils.SonarException;
-import org.sonar.core.persistence.BadDatabaseVersion;
-import org.sonar.core.persistence.DatabaseVersion;
-
-import java.io.IOException;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class DatabaseBatchCompatibilityTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  private DatabaseVersion databaseVersion;
-  private ServerClient server;
-  private Settings settings;
-
-  @Before
-  public void init() throws Exception {
-    databaseVersion = mock(DatabaseVersion.class);
-    when(databaseVersion.getSonarCoreId()).thenReturn("123456");
-
-    server = mock(ServerClient.class);
-    when(server.getURL()).thenReturn("http://localhost:9000");
-    when(server.getServerId()).thenReturn("123456");
-
-    settings = new Settings();
-    settings.setProperty(DatabaseProperties.PROP_URL, "jdbc:postgresql://localhost/foo");
-    settings.setProperty(DatabaseProperties.PROP_USER, "bar");
-  }
-
-  @Test
-  public void shouldFailIfRequiresDowngrade() {
-    when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_DOWNGRADE);
-
-    thrown.expect(BadDatabaseVersion.class);
-    thrown.expectMessage("Database relates to a more recent version of Sonar. Please check your settings (JDBC settings, version of Maven plugin)");
-
-    new DatabaseBatchCompatibility(databaseVersion, server, settings).start();
-  }
-
-  @Test
-  public void shouldFailIfRequiresUpgrade() {
-    when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
-
-    thrown.expect(BadDatabaseVersion.class);
-    thrown.expectMessage("Database must be upgraded.");
-
-    new DatabaseBatchCompatibility(databaseVersion, server, settings).start();
-  }
-
-  @Test
-  public void shouldFailIfNotSameServerId() throws Exception {
-    DatabaseVersion version = mock(DatabaseVersion.class);
-    when(version.getSonarCoreId()).thenReturn("1111111");
-
-    thrown.expect(BadDatabaseVersion.class);
-    thrown.expectMessage("The current batch process and the configured remote server do not share the same DB configuration.");
-    thrown.expectMessage("- Batch side: jdbc:postgresql://localhost/foo (bar / *****)");
-    thrown.expectMessage("- Server side: check the configuration at http://localhost:9000/system");
-
-    new DatabaseBatchCompatibility(version, server, settings).start();
-  }
-
-  @Test
-  public void shouldUseDefaultUserNameWhenFaillingIfNotSameServerIdAndNoUserNameFound() throws Exception {
-    DatabaseVersion version = mock(DatabaseVersion.class);
-    when(version.getSonarCoreId()).thenReturn("1111111");
-
-    settings.removeProperty(DatabaseProperties.PROP_USER);
-
-    thrown.expect(BadDatabaseVersion.class);
-    thrown.expectMessage("- Batch side: jdbc:postgresql://localhost/foo (sonar / *****)");
-
-    new DatabaseBatchCompatibility(version, server, settings).start();
-  }
-
-  @Test
-  public void shouldFailIfCantGetServerId() throws Exception {
-    when(server.getServerId()).thenThrow(new IllegalStateException());
-
-    thrown.expect(IllegalStateException.class);
-
-    new DatabaseBatchCompatibility(mock(DatabaseVersion.class), server, settings).start();
-  }
-
-  @Test
-  public void shouldDoNothingIfUpToDate() {
-    when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.UP_TO_DATE);
-    new DatabaseBatchCompatibility(databaseVersion, server, settings).start();
-    // no error
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DatabaseCompatibilityTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DatabaseCompatibilityTest.java
new file mode 100644 (file)
index 0000000..41489b7
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.database.DatabaseProperties;
+import org.sonar.core.persistence.BadDatabaseVersion;
+import org.sonar.core.persistence.DatabaseVersion;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DatabaseCompatibilityTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  DatabaseVersion databaseVersion;
+  ServerClient server;
+  Settings settings;
+
+  @Before
+  public void init() {
+    server = mock(ServerClient.class);
+    when(server.getURL()).thenReturn("http://localhost:9000");
+    when(server.getServerId()).thenReturn("123456");
+
+    settings = new Settings();
+    settings.setProperty(DatabaseProperties.PROP_URL, "jdbc:postgresql://localhost/foo");
+    settings.setProperty(DatabaseProperties.PROP_USER, "bar");
+    settings.setProperty(CoreProperties.SERVER_ID, "123456");
+    settings.setProperty("sonar.dryRun", false);
+
+    databaseVersion = mock(DatabaseVersion.class);
+  }
+
+  @Test
+  public void shouldFailIfRequiresDowngrade() {
+    when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_DOWNGRADE);
+
+    thrown.expect(BadDatabaseVersion.class);
+    thrown.expectMessage("Database relates to a more recent version of Sonar. Please check your settings (JDBC settings, version of Maven plugin)");
+
+    new DatabaseCompatibility(databaseVersion, server, settings).start();
+  }
+
+  @Test
+  public void shouldFailIfRequiresUpgrade() {
+    when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
+
+    thrown.expect(BadDatabaseVersion.class);
+    thrown.expectMessage("Database must be upgraded.");
+
+    new DatabaseCompatibility(databaseVersion, server, settings).start();
+  }
+
+  @Test
+  public void shouldFailIfNotSameServerId() throws Exception {
+    settings.setProperty(CoreProperties.SERVER_ID, "11111111");
+
+    thrown.expect(BadDatabaseVersion.class);
+    thrown.expectMessage("The current batch process and the configured remote server do not share the same DB configuration.");
+    thrown.expectMessage("- Batch side: jdbc:postgresql://localhost/foo (bar / *****)");
+    thrown.expectMessage("- Server side: check the configuration at http://localhost:9000/system");
+
+    new DatabaseCompatibility(databaseVersion, server, settings).start();
+  }
+
+  @Test
+  public void shouldUseDefaultUserNameWhenFaillingIfNotSameServerIdAndNoUserNameFound() throws Exception {
+    settings.setProperty(CoreProperties.SERVER_ID, "11111111");
+
+    settings.removeProperty(DatabaseProperties.PROP_USER);
+
+    thrown.expect(BadDatabaseVersion.class);
+    thrown.expectMessage("- Batch side: jdbc:postgresql://localhost/foo (sonar / *****)");
+
+    new DatabaseCompatibility(databaseVersion, server, settings).start();
+  }
+
+  @Test
+  public void shouldFailIfCantGetServerId() throws Exception {
+    when(server.getServerId()).thenThrow(new IllegalStateException());
+
+    thrown.expect(IllegalStateException.class);
+
+    new DatabaseCompatibility(mock(DatabaseVersion.class), server, settings).start();
+  }
+
+  @Test
+  public void shouldDoNothingIfUpToDate() {
+    when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.UP_TO_DATE);
+    new DatabaseCompatibility(databaseVersion, server, settings).start();
+    // no error
+  }
+
+  @Test
+  public void should_not_verify_compatibility_if_dry_run() {
+    settings.setProperty(CoreProperties.SERVER_ID, "11111111");
+    settings.setProperty("sonar.dryRun", true);
+
+    new DatabaseCompatibility(databaseVersion, server, settings).start();
+
+    // no failure
+  }
+}
index 2cacc9a493157d929e621c2f6748efcefc9e4496..adad9113158fb89125ae442ae579c2563cdbbf33 100644 (file)
@@ -22,6 +22,8 @@ package org.sonar.batch.bootstrap;
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.junit.Test;
 import org.mockito.Matchers;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.database.model.Snapshot;
@@ -29,9 +31,11 @@ import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.batch.ProjectTree;
+import org.sonar.batch.config.BootstrapSettings;
 import org.sonar.batch.config.ProjectSettings;
 import org.sonar.batch.index.ResourcePersister;
 import org.sonar.core.properties.PropertiesDao;
+import org.sonar.wsclient.Sonar;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.any;
@@ -58,7 +62,8 @@ public class ProjectModuleTest {
         container.addSingleton(extensionInstaller);
         container.addSingleton(projectTree);
         container.addSingleton(resourcePersister);
-        container.addSingleton(mock(PropertiesDao.class));
+        container.addSingleton(mock(Sonar.class));
+        container.addSingleton(mock(BootstrapSettings.class));
       }
     };
 
index eede4877e50f043a1baaa5676c293e49797662e9..753a09921a8427914b17de8bc3d83ca89a1bb4fe 100644 (file)
@@ -37,7 +37,7 @@ public class ProjectSettingsTest {
     grandParent.addSubProject(parent);
     parent.addSubProject(child);
 
-    List<ProjectDefinition> hierarchy = ProjectSettings.getOrderedProjects(child);
+    List<ProjectDefinition> hierarchy = ProjectSettings.getTopDownParentProjects(child);
     assertThat(hierarchy.get(0), Is.is(grandParent));
     assertThat(hierarchy.get(1), Is.is(parent));
     assertThat(hierarchy.get(2), Is.is(child));
index 793723d6af005c375254c7d87306f7f47aa1d5c0..3ed5fba82feb9f176d8d52b908696f75def1d28c 100644 (file)
@@ -66,17 +66,6 @@ public class DatabaseVersion implements BatchComponent, ServerComponent {
     }
   }
 
-  public String getSonarCoreId() {
-    SqlSession session = mybatis.openSession();
-    try {
-      PropertyDto serverIdProperty = session.getMapper(PropertiesMapper.class).selectByKey(new PropertyDto().setKey(CoreProperties.SERVER_ID));
-      // this property can't be NULL
-      return serverIdProperty.getValue();
-    } finally {
-      MyBatis.closeQuietly(session);
-    }
-  }
-
   public Status getStatus() {
     return getStatus(getVersion(), LAST_VERSION);
   }
index e83bf0c0cb3b72796edc2185888df8790f170bfa..611400aab516c95c5226ef160e118576c731a54f 100644 (file)
@@ -41,7 +41,7 @@ public class DbTemplate implements ServerComponent {
   private static final Logger LOG = LoggerFactory.getLogger(DbTemplate.class);
 
   public DbTemplate copyTable(DataSource source, DataSource dest, String table, String... whereClauses) {
-    LOG.info("Copy table " + table);
+    LOG.debug("Copy table %s", table);
 
     String selectQuery = "select * from " + table;
     if (whereClauses.length > 0) {
index 82d3736f629afe14775771e12d9e9c811fcafebe..5f6c01e442968025980fdd253e2ecb258a76521f 100644 (file)
@@ -66,6 +66,7 @@ public class DefaultDatabase implements Database {
       initSettings();
       initDatasource();
       checkConnection();
+      doAfterStart();
       return this;
 
     } catch (Exception e) {
@@ -73,6 +74,13 @@ public class DefaultDatabase implements Database {
     }
   }
 
+  /**
+   * Override to execute post-startup code.
+   */
+  protected void doAfterStart() {
+
+  }
+
   @VisibleForTesting
   void initSettings() {
     initProperties();
@@ -93,7 +101,7 @@ public class DefaultDatabase implements Database {
     if (dialect == null) {
       throw new IllegalStateException("Can not guess the JDBC dialect. Please check the property sonar.jdbc.url.");
     }
-    if (H2.ID.equals(dialect.getId())) {
+    if (H2.ID.equals(dialect.getId()) && !settings.getBoolean("sonar.dryRun")) {
       LoggerFactory.getLogger(DefaultDatabase.class).warn("H2 database should be used for evaluation purpose only");
     }
     if (!properties.containsKey("sonar.jdbc.driverClassName")) {
index 9c8b525248262c3706945daf1fc9fda3929736f5..6530140510da24403bcb34805164aa9e09f86030 100644 (file)
@@ -63,10 +63,6 @@ public class DryRunDatabaseFactory implements ServerComponent {
   }
 
   private void copy(DataSource source, DataSource dest, Integer resourceId) {
-    String notSecured = "NOT (prop_key LIKE '%.secured')";
-    String defaultProperty = "((user_id IS NULL) AND (resource_id IS NULL))";
-    String projectProperty = (null == resourceId) ? "" : " OR (resource_id='" + resourceId + "')";
-
     new DbTemplate()
         .copyTable(source, dest, "active_rules")
         .copyTable(source, dest, "active_rule_parameters")
@@ -74,7 +70,6 @@ public class DryRunDatabaseFactory implements ServerComponent {
         .copyTable(source, dest, "characteristic_edges")
         .copyTable(source, dest, "characteristic_properties")
         .copyTable(source, dest, "metrics")
-        .copyTable(source, dest, "properties", notSecured, defaultProperty + projectProperty)
         .copyTable(source, dest, "quality_models")
         .copyTable(source, dest, "rules")
         .copyTable(source, dest, "rules_parameters")
index df1dce3e4265321ea12ea3c55ebddc5e3ccaa2f6..a0278eed475da02f3aab1b1f1fdfb6cb64a6887a 100644 (file)
  */
 package org.sonar.core.persistence;
 
-import org.hamcrest.core.Is;
+
 import org.junit.Test;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertThat;
+import static org.fest.assertions.Assertions.assertThat;
 
 public class DatabaseVersionTest extends AbstractDaoTestCase {
   @Test
@@ -33,7 +31,7 @@ public class DatabaseVersionTest extends AbstractDaoTestCase {
 
     Integer version = new DatabaseVersion(getMyBatis()).getVersion();
 
-    assertThat(version, Is.is(123));
+    assertThat(version).isEqualTo(123);
   }
 
   @Test
@@ -42,23 +40,14 @@ public class DatabaseVersionTest extends AbstractDaoTestCase {
 
     Integer version = new DatabaseVersion(getMyBatis()).getVersion();
 
-    assertThat(version, nullValue());
-  }
-
-  @Test
-  public void getSonarCoreId() {
-    setupData("getSonarCoreId");
-
-    String sonarCoreId = new DatabaseVersion(getMyBatis()).getSonarCoreId();
-
-    assertThat(sonarCoreId, is("123456"));
+    assertThat(version).isNull();
   }
 
   @Test
   public void getStatus() {
-    assertThat(DatabaseVersion.getStatus(null, 150), is(DatabaseVersion.Status.FRESH_INSTALL));
-    assertThat(DatabaseVersion.getStatus(123, 150), is(DatabaseVersion.Status.REQUIRES_UPGRADE));
-    assertThat(DatabaseVersion.getStatus(150, 150), is(DatabaseVersion.Status.UP_TO_DATE));
-    assertThat(DatabaseVersion.getStatus(200, 150), is(DatabaseVersion.Status.REQUIRES_DOWNGRADE));
+    assertThat(DatabaseVersion.getStatus(null, 150)).isEqualTo(DatabaseVersion.Status.FRESH_INSTALL);
+    assertThat(DatabaseVersion.getStatus(123, 150)).isEqualTo(DatabaseVersion.Status.REQUIRES_UPGRADE);
+    assertThat(DatabaseVersion.getStatus(150, 150)).isEqualTo(DatabaseVersion.Status.UP_TO_DATE);
+    assertThat(DatabaseVersion.getStatus(200, 150)).isEqualTo(DatabaseVersion.Status.REQUIRES_DOWNGRADE);
   }
 }
index b44340a1fc59dab88ee8f2c7218f983783eb759a..aa284c22e13e30f2d84e8bab0c88bac544e2e8c2 100644 (file)
@@ -66,8 +66,8 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(1);
     dataSource = createDatabase(database);
 
-    assertThat(rowCount("PROPERTIES")).isEqualTo(2);
-    assertThat(rowCount("PROJECTS")).isZero();
+    assertThat(rowCount("metrics")).isEqualTo(2);
+    assertThat(rowCount("projects")).isZero();
   }
 
   private BasicDataSource createDatabase(byte[] db) throws IOException {
diff --git a/sonar-core/src/test/resources/org/sonar/core/persistence/DatabaseVersionTest/getSonarCoreId.xml b/sonar-core/src/test/resources/org/sonar/core/persistence/DatabaseVersionTest/getSonarCoreId.xml
deleted file mode 100644 (file)
index 44b1e8e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<dataset>
-  <properties id="1" prop_key="sonar.core.id" resource_id="[null]" text_value="123456" user_id="[null]"/>
-</dataset>
index 0e2665189066c6e4b323bf78490bd998c519b0c9..3f7360668de98b72b2ef0b9384c3bf9eaab8ab84 100644 (file)
@@ -1,9 +1,8 @@
 <dataset>
-  <properties id="1" prop_key="resourceProperty" text_value="value1" resource_id="1" user_id="[null]"/>
-  <properties id="2" prop_key="resourceProperty" text_value="value2" resource_id="2" user_id="[null]"/>
-  <properties id="3" prop_key="globalProperty" text_value="value3" resource_id="[null]" user_id="[null]"/>
-  <properties id="4" prop_key="userProperty" text_value="value4" resource_id="[null]" user_id="1"/>
-  <properties id="5" prop_key="property.secured" text_value="value5" resource_id="[null]" user_id="[null]"/>
+  <metrics id="1" name="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
+           enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false" delete_historical_data="[null]" />
+  <metrics id="2" name="coverage" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
+           enabled="true" worst_value="0" optimized_best_value="true" best_value="100" direction="1" hidden="false" delete_historical_data="[null]" />
 
   <rules_profiles id="1" name="Sonar way with Findbugs" language="java" parent_name="" version="1"
                   used_profile="false"/>
index 307baa2e5fc639ac9b10d16e0b67a3e42de597a8..8043a7e9e354b93b4a92aaf21204e29f41682adf 100644 (file)
@@ -296,23 +296,23 @@ public class Settings implements BatchComponent, ServerComponent {
   }
 
   public final Settings setProperty(String key, @Nullable Boolean value) {
-    return setProperty(key, value==null ? null : String.valueOf(value));
+    return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
   public final Settings setProperty(String key, @Nullable Integer value) {
-    return setProperty(key, value==null ? null : String.valueOf(value));
+    return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
   public final Settings setProperty(String key, @Nullable Long value) {
-    return setProperty(key, value==null ? null : String.valueOf(value));
+    return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
   public final Settings setProperty(String key, @Nullable Double value) {
-    return setProperty(key, value==null ? null : String.valueOf(value));
+    return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
   public final Settings setProperty(String key, @Nullable Float value) {
-    return setProperty(key, value==null ? null : String.valueOf(value));
+    return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
   public final Settings setProperty(String key, @Nullable Date date) {
@@ -346,6 +346,13 @@ public class Settings implements BatchComponent, ServerComponent {
     return addProperties(props);
   }
 
+  public final Settings setProperties(Settings s) {
+    if (s.properties==null) {
+      return clear();
+    }
+    return setProperties(Maps.newHashMap(s.properties));
+  }
+
   public final Settings setProperty(String key, @Nullable Date date, boolean includeTime) {
     return setProperty(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date));
   }
index 05bc74c95a3feada886b564e9fca1757285469bb..a2b9885aaf2a32d6d0c095d191d42e5fcc0a4106 100644 (file)
 # 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::PropertiesController < Api::ApiController
 
   before_filter :admin_required, :only => [:create, :update, :destroy]
 
-  # curl http://localhost:9000/api/properties -v
+  # GET /api/properties/index?[resource=<resource id or key>]
+  # Does NOT manage default values.
   def index
-    properties = Property.find(:all, :conditions => ['resource_id is null and user_id is null']).select do |property|
-      viewable?(property.key)
+    keys=Set.new
+    properties=[]
+
+    # project properties
+    if params[:resource]
+      resource=Project.by_key(params[:resource])
+      if resource
+        # bottom-up projects
+        projects=[resource].concat(resource.ancestor_projects)
+        projects.each do |project|
+          Property.find(:all, :conditions => ['resource_id=? and user_id is null', project.id]).each do |prop|
+            properties<<prop if keys.add? prop.key
+          end
+        end
+      end
     end
+
+    # global properties
+    Property.find(:all, :conditions => 'resource_id is null and user_id is null').each do |prop|
+      properties<<prop if keys.add? prop.key
+    end
+
+    # apply security
+    properties = properties.select{|prop| allowed?(prop.key)}
+
     respond_to do |format|
       format.json { render :json => jsonp(to_json(properties)) }
       format.xml { render :xml => to_xml(properties) }
     end
   end
 
-  # curl http://localhost:9000/api/properties/<key>[?resource=<resource>] -v
+  # GET /api/properties/<key>[?resource=<resource>]
   def show
     key = params[:id]
     resource_id_or_key = params[:resource]
@@ -55,7 +75,7 @@ class Api::PropertiesController < Api::ApiController
         format.text { render :text => message, :status => 200 }
       end
     end
-    access_denied unless viewable?(key)
+    access_denied unless allowed?(key)
     respond_to do |format|
       format.json { render :json => jsonp(to_json([prop])) }
       format.xml { render :xml => to_xml([prop]) }
@@ -122,8 +142,8 @@ class Api::PropertiesController < Api::ApiController
     end
   end
 
-  def viewable?(property_key)
-    !property_key.to_s.index('.secured') || is_admin?
+  def allowed?(property_key)
+    !property_key.end_with?('.secured') || is_admin?
   end
 
 end
index 4acf953c17c7384de245f739e1a2925e3dc20dba..902ad57de758609cd3b8d419f8f192c2182abe40 100644 (file)
@@ -131,7 +131,7 @@ class Project < ActiveRecord::Base
                                          ' from project_measures m, snapshots s ' +
                                          ' where s.id=m.snapshot_id and ' +
                                          " s.status='%s' and " +
-                                         ' s.project_id=%s and m.metric_id=%s ', Snapshot::STATUS_PROCESSED, self.id, metric_id]) +
+                                         ' s.project_id=%s and m.metric_id=%s ', 'P', self.id, metric_id]) +
       ' and m.rule_id IS NULL and m.rule_priority IS NULL' +
       ' and m.person_id IS NULL' +
       ' order by s.created_at'
index 7d332a49864bfd2b058c1400b5a86499fbedd426..c3639e781a3a9e69e54b3edeeb848154103b83de 100644 (file)
  */
 package org.sonar.wsclient.services;
 
+import org.junit.Test;
+
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
 
-import org.junit.Test;
-
 public class PropertyQueryTest extends QueryTestCase {
 
   @Test
-  public void all() {
-    assertThat(PropertyQuery.createForAll().getUrl(), is("/api/properties?"));
-    assertThat(PropertyQuery.createForAll().getModelClass().getName(), is(Property.class.getName()));
+  public void test_global_properties() {
+    PropertyQuery query = PropertyQuery.createForAll();
+    assertThat(query.getUrl(), is("/api/properties?"));
+    assertThat(query.getModelClass().getName(), is(Property.class.getName()));
+  }
+
+  @Test
+  public void test_project_properties() {
+    PropertyQuery query = PropertyQuery.createForAll().setResourceKeyOrId("org.apache:struts");
+    assertThat(query.getUrl(), is("/api/properties?resource=org.apache%3Astruts&"));
+    assertThat(query.getModelClass().getName(), is(Property.class.getName()));
   }
 
   @Test
-  public void byKey() {
-    assertThat(PropertyQuery.createForKey("myprop").getUrl(), is("/api/properties/myprop?"));
-    assertThat(PropertyQuery.createForKey("myprop").getModelClass().getName(), is(Property.class.getName()));
+  public void test_global_property() {
+    PropertyQuery query = PropertyQuery.createForKey("myprop");
+    assertThat(query.getUrl(), is("/api/properties/myprop?"));
+    assertThat(query.getModelClass().getName(), is(Property.class.getName()));
   }
 
   @Test
-  public void byKeyAndResource() {
-    assertThat(PropertyQuery.createForResource("myprop", "my:resource").getUrl(), is("/api/properties/myprop?resource=my%3Aresource&"));
-    assertThat(PropertyQuery.createForResource("myprop", "my:resource").getModelClass().getName(), is(Property.class.getName()));
+  public void test_project_property() {
+    PropertyQuery query = PropertyQuery.createForResource("myprop", "my:resource");
+    assertThat(query.getUrl(), is("/api/properties/myprop?resource=my%3Aresource&"));
+    assertThat(query.getModelClass().getName(), is(Property.class.getName()));
   }
 }