]> source.dussan.org Git - sonarqube.git/commitdiff
Move some classes from org.sonar.batch.bootstrap to org.sonar.batch.scan
authorSimon Brandhof <simon.brandhof@gmail.com>
Thu, 14 Mar 2013 17:34:40 +0000 (18:34 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Thu, 14 Mar 2013 17:34:52 +0000 (18:34 +0100)
29 files changed:
sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExclusions.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectReactorReady.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectSettings.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/UnsupportedProperties.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/scan/DurationLabel.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectExclusions.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/package-info.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/DurationLabelTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectExclusionsTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectReactorReadyTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectSettingsTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/UnsupportedPropertiesTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/scan/DurationLabelTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java

index b2317a2e8120facabbdc3e4826c5e8b779665b51..38334bb54c768d2eaf2b8068e2b9484b5a86b375 100644 (file)
@@ -25,7 +25,7 @@ import org.apache.commons.lang.ObjectUtils;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.api.resources.Project;
-import org.sonar.batch.bootstrap.ProjectReactorReady;
+import org.sonar.batch.scan.ProjectReactorReady;
 
 import java.util.List;
 import java.util.Map;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java
deleted file mode 100644 (file)
index a8274c5..0000000
+++ /dev/null
@@ -1,136 +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 com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.lang.StringUtils;
-
-import java.text.MessageFormat;
-
-public class DurationLabel {
-
-  private String suffixAgo = "ago";
-  private String seconds = "less than a minute";
-  private String minute = "about a minute";
-  private String minutes = "{0} minutes";
-  private String hour = "about an hour";
-  private String hours = "{0} hours";
-  private String day = "a day";
-  private String days = "{0} days";
-  private String month = "about a month";
-  private String months = "{0} months";
-  private String year = "about a year";
-  private String years = "{0} years";
-
-  public String label(long durationInMillis) {
-    double nbSeconds = durationInMillis / 1000.0;
-    double nbMinutes = nbSeconds / 60;
-    double nbHours = nbMinutes / 60;
-    double nbDays = nbHours / 24;
-    double nbYears = nbDays / 365;
-    String message = getMessage(nbSeconds, nbMinutes, nbHours, nbDays, nbYears);
-    return join(message, suffixAgo);
-  }
-
-  private String getMessage(double nbSeconds, double nbMinutes, double nbHours, double nbDays, double nbYears) {
-    String message = MessageFormat.format(this.years, Math.floor(nbYears));
-    if (nbSeconds < 45) {
-      message = this.seconds;
-    } else if (nbSeconds < 90) {
-      message = this.minute;
-    } else if (nbMinutes < 45) {
-      message = MessageFormat.format(this.minutes, Math.round(nbMinutes));
-    } else if (nbMinutes < 90) {
-      message = this.hour;
-    } else if (nbHours < 24) {
-      message = MessageFormat.format(this.hours, Math.round(nbHours));
-    } else if (nbHours < 48) {
-      message = this.day;
-    } else if (nbDays < 30) {
-      message = MessageFormat.format(this.days, Math.floor(nbDays));
-    } else if (nbDays < 60) {
-      message = this.month;
-    } else if (nbDays < 365) {
-      message = MessageFormat.format(this.months, Math.floor(nbDays / 30));
-    } else if (nbYears < 2) {
-      message = this.year;
-    }
-    return message;
-  }
-
-  @VisibleForTesting
-  String join(String time, String suffix) {
-    StringBuilder joined = new StringBuilder();
-    joined.append(time);
-    if (StringUtils.isNotBlank(suffix)) {
-      joined.append(' ').append(suffix);
-    }
-    return joined.toString();
-  }
-
-  public String getSuffixAgo() {
-    return suffixAgo;
-  }
-
-  public String getSeconds() {
-    return seconds;
-  }
-
-  public String getMinute() {
-    return minute;
-  }
-
-  public String getMinutes() {
-    return minutes;
-  }
-
-  public String getHour() {
-    return hour;
-  }
-
-  public String getHours() {
-    return hours;
-  }
-
-  public String getDay() {
-    return day;
-  }
-
-  public String getDays() {
-    return days;
-  }
-
-  public String getMonth() {
-    return month;
-  }
-
-  public String getMonths() {
-    return months;
-  }
-
-  public String getYear() {
-    return year;
-  }
-
-  public String getYears() {
-    return years;
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExclusions.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExclusions.java
deleted file mode 100644 (file)
index 5310b17..0000000
+++ /dev/null
@@ -1,97 +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.task.TaskComponent;
-
-import org.apache.commons.lang.ArrayUtils;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.bootstrap.ProjectBuilder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.config.Settings;
-
-/**
- * Exclude the sub-projects as defined by the properties sonar.skippedModules and sonar.includedModules
- *
- * @since 2.12
- */
-public class ProjectExclusions implements TaskComponent {
-
-  private static final Logger LOG = LoggerFactory.getLogger(ProjectExclusions.class);
-
-  private Settings settings;
-  private ProjectReactor reactor;
-
-  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;
-  }
-
-  public ProjectExclusions(Settings settings, ProjectReactor reactor) {
-    this(settings, reactor, new ProjectBuilder[0]);
-  }
-
-  public void start() {
-    if (reactor.getProjects().size() > 0 && StringUtils.isNotBlank(reactor.getProjects().get(0).getKey())) {
-      LOG.info("Apply project exclusions");
-      for (ProjectDefinition project : reactor.getProjects()) {
-        if (isExcluded(key(project), project == reactor.getRoot())) {
-          exclude(project);
-        }
-      }
-    }
-  }
-
-  private boolean isExcluded(String projectKey, boolean isRoot) {
-    String[] includedKeys = settings.getStringArray("sonar.includedModules");
-    boolean excluded = false;
-    if (!isRoot && includedKeys.length > 0) {
-      excluded = !ArrayUtils.contains(includedKeys, projectKey);
-    }
-    String skippedModulesProperty = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY;
-    if (!excluded) {
-      String[] excludedKeys = settings.getStringArray(skippedModulesProperty);
-      excluded = ArrayUtils.contains(excludedKeys, projectKey);
-    }
-    if (excluded && isRoot) {
-      throw new IllegalArgumentException("The root project can't be excluded. Please check the parameters " + skippedModulesProperty + " and sonar.includedModules.");
-    }
-    return excluded;
-  }
-
-  private void exclude(ProjectDefinition project) {
-    LOG.info(String.format("Exclude project: %s [%s]", project.getName(), project.getKey()));
-    project.remove();
-  }
-
-  static String key(ProjectDefinition project) {
-    String key = project.getKey();
-    if (key.contains(":")) {
-      return StringUtils.substringAfter(key, ":");
-    }
-    return key;
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java
deleted file mode 100644 (file)
index 144dd25..0000000
+++ /dev/null
@@ -1,91 +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.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.Semaphores;
-import org.sonar.api.utils.SonarException;
-import org.sonar.batch.ProjectTree;
-
-public class ProjectLock {
-
-  private static final Logger LOG = LoggerFactory.getLogger(ProjectLock.class);
-
-  private final Semaphores semaphores;
-  private final ProjectTree projectTree;
-  private final Settings settings;
-
-  public ProjectLock(Semaphores semaphores, ProjectTree projectTree, Settings settings) {
-    this.semaphores = semaphores;
-    this.projectTree = projectTree;
-    this.settings = settings;
-  }
-
-  public void start() {
-    if (!isInDryRunMode() && StringUtils.isNotBlank(getProject().getKey())) {
-      Semaphores.Semaphore semaphore = acquire();
-      if (!semaphore.isLocked()) {
-        LOG.error(getErrorMessage(semaphore));
-        throw new SonarException("The project is already being analysed.");
-      }
-    }
-  }
-
-  private String getErrorMessage(Semaphores.Semaphore semaphore) {
-    long duration = semaphore.getDurationSinceLocked();
-    DurationLabel durationLabel = new DurationLabel();
-    String durationDisplay = durationLabel.label(duration);
-
-    return "It looks like an analysis of '" + getProject().getName() + "' is already running (started " + durationDisplay + ").";
-  }
-
-  public void stop() {
-    if (!isInDryRunMode()) {
-      release();
-    }
-  }
-
-  private Semaphores.Semaphore acquire() {
-    LOG.debug("Acquire semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
-    return semaphores.acquire(getSemaphoreKey(), 15, 10);
-  }
-
-  private void release() {
-    LOG.debug("Release semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
-    semaphores.release(getSemaphoreKey());
-  }
-
-  private String getSemaphoreKey() {
-    return "batch-" + getProject().getKey();
-  }
-
-  private Project getProject() {
-    return projectTree.getRootProject();
-  }
-
-  private boolean isInDryRunMode() {
-    return settings.getBoolean(CoreProperties.DRY_RUN);
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectReactorReady.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectReactorReady.java
deleted file mode 100644 (file)
index 08a3907..0000000
+++ /dev/null
@@ -1,47 +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.batch.bootstrap.ProjectBuilder;
-
-/**
- * Barrier to control the project lifecycle :
- * <p/>
- * <ul>
- * <li>initialize the project configuration by executing ProjectBuilder extensions</li>
- * <li>apply sub-project exclusions (sonar.skippedModules, ...)</li>
- * <li>---- this barrier ----</li>
- * <li>run optional dry run database</li>
- * <li>connect to dry-run or remote database</li>
- * </ul>
- */
-public class ProjectReactorReady {
-
-  public ProjectReactorReady(ProjectExclusions exclusions, ProjectBuilder[] projectBuilders) {
-  }
-
-  public ProjectReactorReady(ProjectExclusions exclusions) {
-    this(exclusions, new ProjectBuilder[0]);
-  }
-
-  public void start() {
-
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectSettings.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectSettings.java
deleted file mode 100644 (file)
index eb5da0f..0000000
+++ /dev/null
@@ -1,107 +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 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.Settings;
-
-import javax.annotation.Nullable;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * @since 2.12
- */
-public class ProjectSettings extends Settings {
-
-  private Configuration deprecatedCommonsConf;
-
-  public ProjectSettings(BatchSettings batchSettings, ProjectDefinition project, Configuration deprecatedCommonsConf) {
-    super(batchSettings.getDefinitions());
-
-    LoggerFactory.getLogger(ProjectSettings.class).info("Load module settings");
-    this.deprecatedCommonsConf = deprecatedCommonsConf;
-    init(project, batchSettings);
-  }
-
-  private ProjectSettings init(ProjectDefinition project, BatchSettings batchSettings) {
-    addProjectProperties(project, batchSettings);
-    addBuildProperties(project);
-    addEnvironmentVariables();
-    addSystemProperties();
-    return this;
-  }
-
-  private void addProjectProperties(ProjectDefinition project, BatchSettings batchSettings) {
-    String branch = batchSettings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
-    String projectKey = project.getKey();
-    if (StringUtils.isNotBlank(branch)) {
-      projectKey = String.format("%s:%s", projectKey, branch);
-    }
-    addProperties(batchSettings.getProperties());
-    Map<String, String> moduleProps = batchSettings.getModuleProperties(projectKey);
-    if (moduleProps != null) {
-      for (Map.Entry<String, String> entry : moduleProps.entrySet()) {
-        setProperty(entry.getKey(), entry.getValue());
-      }
-    }
-  }
-
-  private void addBuildProperties(ProjectDefinition project) {
-    List<ProjectDefinition> orderedProjects = getTopDownParentProjects(project);
-    for (ProjectDefinition p : orderedProjects) {
-      addProperties(p.getProperties());
-    }
-  }
-
-  /**
-   * From root to given project
-   */
-  static List<ProjectDefinition> getTopDownParentProjects(ProjectDefinition project) {
-    List<ProjectDefinition> result = Lists.newArrayList();
-    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();
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/UnsupportedProperties.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/UnsupportedProperties.java
deleted file mode 100644 (file)
index 6d66612..0000000
+++ /dev/null
@@ -1,41 +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;
-
-public class UnsupportedProperties implements BatchComponent {
-  private final Settings settings;
-
-  public UnsupportedProperties(Settings settings) {
-    this.settings = settings;
-  }
-
-  public void start() {
-    verify("sonar.light", "The property 'sonar.light' is no longer supported. Please use 'sonar.dynamicAnalysis'");
-  }
-
-  private void verify(String key, String message) {
-    if (settings.hasKey(key)) {
-      throw new IllegalArgumentException(message);
-    }
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/DurationLabel.java b/sonar-batch/src/main/java/org/sonar/batch/scan/DurationLabel.java
new file mode 100644 (file)
index 0000000..1691a9a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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 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.scan;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang.StringUtils;
+
+import java.text.MessageFormat;
+
+class DurationLabel {
+
+  private String suffixAgo = "ago";
+  private String seconds = "less than a minute";
+  private String minute = "about a minute";
+  private String minutes = "{0} minutes";
+  private String hour = "about an hour";
+  private String hours = "{0} hours";
+  private String day = "a day";
+  private String days = "{0} days";
+  private String month = "about a month";
+  private String months = "{0} months";
+  private String year = "about a year";
+  private String years = "{0} years";
+
+  String label(long durationInMillis) {
+    double nbSeconds = durationInMillis / 1000.0;
+    double nbMinutes = nbSeconds / 60;
+    double nbHours = nbMinutes / 60;
+    double nbDays = nbHours / 24;
+    double nbYears = nbDays / 365;
+    String message = getMessage(nbSeconds, nbMinutes, nbHours, nbDays, nbYears);
+    return join(message, suffixAgo);
+  }
+
+  private String getMessage(double nbSeconds, double nbMinutes, double nbHours, double nbDays, double nbYears) {
+    String message = MessageFormat.format(this.years, Math.floor(nbYears));
+    if (nbSeconds < 45) {
+      message = this.seconds;
+    } else if (nbSeconds < 90) {
+      message = this.minute;
+    } else if (nbMinutes < 45) {
+      message = MessageFormat.format(this.minutes, Math.round(nbMinutes));
+    } else if (nbMinutes < 90) {
+      message = this.hour;
+    } else if (nbHours < 24) {
+      message = MessageFormat.format(this.hours, Math.round(nbHours));
+    } else if (nbHours < 48) {
+      message = this.day;
+    } else if (nbDays < 30) {
+      message = MessageFormat.format(this.days, Math.floor(nbDays));
+    } else if (nbDays < 60) {
+      message = this.month;
+    } else if (nbDays < 365) {
+      message = MessageFormat.format(this.months, Math.floor(nbDays / 30));
+    } else if (nbYears < 2) {
+      message = this.year;
+    }
+    return message;
+  }
+
+  @VisibleForTesting
+  String join(String time, String suffix) {
+    StringBuilder joined = new StringBuilder();
+    joined.append(time);
+    if (StringUtils.isNotBlank(suffix)) {
+      joined.append(' ').append(suffix);
+    }
+    return joined.toString();
+  }
+
+  String getSuffixAgo() {
+    return suffixAgo;
+  }
+
+  String getSeconds() {
+    return seconds;
+  }
+
+  String getMinute() {
+    return minute;
+  }
+
+  String getMinutes() {
+    return minutes;
+  }
+
+  String getHour() {
+    return hour;
+  }
+
+  String getHours() {
+    return hours;
+  }
+
+  String getDay() {
+    return day;
+  }
+
+  String getDays() {
+    return days;
+  }
+
+  String getMonth() {
+    return month;
+  }
+
+  String getMonths() {
+    return months;
+  }
+
+  String getYear() {
+    return year;
+  }
+
+  String getYears() {
+    return years;
+  }
+
+}
index 00b9ccb3746eb4dc51e77bfa1dece074f536fd0a..b85b7e75bb4a514b473b2c8b4a3c25b1ea386c23 100644 (file)
@@ -41,8 +41,6 @@ import org.sonar.batch.ResourceFilters;
 import org.sonar.batch.ViolationFilters;
 import org.sonar.batch.bootstrap.ExtensionInstaller;
 import org.sonar.batch.bootstrap.ExtensionUtils;
-import org.sonar.batch.bootstrap.ProjectSettings;
-import org.sonar.batch.bootstrap.UnsupportedProperties;
 import org.sonar.batch.components.TimeMachineConfiguration;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.DefaultIndex;
@@ -78,10 +76,10 @@ public class ModuleScanContainer extends ComponentContainer {
         moduleDefinition,
         module.getConfiguration(),
         module,
-        ProjectSettings.class);
+        ModuleSettings.class);
 
     // hack to initialize commons-configuration before ExtensionProviders
-    get(ProjectSettings.class);
+    get(ModuleSettings.class);
 
     add(
         EventBus.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java
new file mode 100644 (file)
index 0000000..bb5b064
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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.scan;
+
+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.Settings;
+import org.sonar.batch.bootstrap.BatchSettings;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @since 2.12
+ */
+public class ModuleSettings extends Settings {
+
+  private final Configuration deprecatedCommonsConf;
+
+  public ModuleSettings(BatchSettings batchSettings, ProjectDefinition project, Configuration deprecatedCommonsConf) {
+    super(batchSettings.getDefinitions());
+
+    LoggerFactory.getLogger(ModuleSettings.class).info("Load module settings");
+    this.deprecatedCommonsConf = deprecatedCommonsConf;
+    init(project, batchSettings);
+  }
+
+  private ModuleSettings init(ProjectDefinition project, BatchSettings batchSettings) {
+    addProjectProperties(project, batchSettings);
+    addBuildProperties(project);
+    addEnvironmentVariables();
+    addSystemProperties();
+    return this;
+  }
+
+  private void addProjectProperties(ProjectDefinition project, BatchSettings batchSettings) {
+    String branch = batchSettings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
+    String projectKey = project.getKey();
+    if (StringUtils.isNotBlank(branch)) {
+      projectKey = String.format("%s:%s", projectKey, branch);
+    }
+    addProperties(batchSettings.getProperties());
+    Map<String, String> moduleProps = batchSettings.getModuleProperties(projectKey);
+    if (moduleProps != null) {
+      for (Map.Entry<String, String> entry : moduleProps.entrySet()) {
+        setProperty(entry.getKey(), entry.getValue());
+      }
+    }
+  }
+
+  private void addBuildProperties(ProjectDefinition project) {
+    List<ProjectDefinition> orderedProjects = getTopDownParentProjects(project);
+    for (ProjectDefinition p : orderedProjects) {
+      addProperties(p.getProperties());
+    }
+  }
+
+  /**
+   * From root to given project
+   */
+  static List<ProjectDefinition> getTopDownParentProjects(ProjectDefinition project) {
+    List<ProjectDefinition> result = Lists.newArrayList();
+    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();
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectExclusions.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectExclusions.java
new file mode 100644 (file)
index 0000000..3e0d1f1
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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.scan;
+
+import org.sonar.api.task.TaskComponent;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectBuilder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+
+import javax.annotation.Nullable;
+
+/**
+ * Exclude the sub-projects as defined by the properties sonar.skippedModules and sonar.includedModules
+ *
+ * @since 2.12
+ */
+public class ProjectExclusions implements TaskComponent {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ProjectExclusions.class);
+
+  private Settings settings;
+  private ProjectReactor reactor;
+
+  public ProjectExclusions(Settings settings, ProjectReactor reactor,
+      // exclusions are applied when the project is completely defined by extensions
+      @Nullable ProjectBuilder[] projectBuilders) {
+    this.settings = settings;
+    this.reactor = reactor;
+  }
+
+  public ProjectExclusions(Settings settings, ProjectReactor reactor) {
+    this(settings, reactor, new ProjectBuilder[0]);
+  }
+
+  public void start() {
+    if (reactor.getProjects().size() > 0 && StringUtils.isNotBlank(reactor.getProjects().get(0).getKey())) {
+      LOG.info("Apply project exclusions");
+      for (ProjectDefinition project : reactor.getProjects()) {
+        if (isExcluded(key(project), project == reactor.getRoot())) {
+          exclude(project);
+        }
+      }
+    }
+  }
+
+  private boolean isExcluded(String projectKey, boolean isRoot) {
+    String[] includedKeys = settings.getStringArray("sonar.includedModules");
+    boolean excluded = false;
+    if (!isRoot && includedKeys.length > 0) {
+      excluded = !ArrayUtils.contains(includedKeys, projectKey);
+    }
+    String skippedModulesProperty = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY;
+    if (!excluded) {
+      String[] excludedKeys = settings.getStringArray(skippedModulesProperty);
+      excluded = ArrayUtils.contains(excludedKeys, projectKey);
+    }
+    if (excluded && isRoot) {
+      throw new IllegalArgumentException("The root project can't be excluded. Please check the parameters " + skippedModulesProperty + " and sonar.includedModules.");
+    }
+    return excluded;
+  }
+
+  private void exclude(ProjectDefinition project) {
+    LOG.info(String.format("Exclude project: %s [%s]", project.getName(), project.getKey()));
+    project.remove();
+  }
+
+  static String key(ProjectDefinition project) {
+    String key = project.getKey();
+    if (key.contains(":")) {
+      return StringUtils.substringAfter(key, ":");
+    }
+    return key;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java
new file mode 100644 (file)
index 0000000..5a81372
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.scan;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.Semaphores;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.ProjectTree;
+
+public class ProjectLock {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ProjectLock.class);
+
+  private final Semaphores semaphores;
+  private final ProjectTree projectTree;
+  private final Settings settings;
+
+  public ProjectLock(Semaphores semaphores, ProjectTree projectTree, Settings settings) {
+    this.semaphores = semaphores;
+    this.projectTree = projectTree;
+    this.settings = settings;
+  }
+
+  public void start() {
+    if (!isInDryRunMode() && StringUtils.isNotBlank(getProject().getKey())) {
+      Semaphores.Semaphore semaphore = acquire();
+      if (!semaphore.isLocked()) {
+        LOG.error(getErrorMessage(semaphore));
+        throw new SonarException("The project is already being analysed.");
+      }
+    }
+  }
+
+  private String getErrorMessage(Semaphores.Semaphore semaphore) {
+    long duration = semaphore.getDurationSinceLocked();
+    DurationLabel durationLabel = new DurationLabel();
+    String durationDisplay = durationLabel.label(duration);
+
+    return "It looks like an analysis of '" + getProject().getName() + "' is already running (started " + durationDisplay + ").";
+  }
+
+  public void stop() {
+    if (!isInDryRunMode()) {
+      release();
+    }
+  }
+
+  private Semaphores.Semaphore acquire() {
+    LOG.debug("Acquire semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
+    return semaphores.acquire(getSemaphoreKey(), 15, 10);
+  }
+
+  private void release() {
+    LOG.debug("Release semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
+    semaphores.release(getSemaphoreKey());
+  }
+
+  private String getSemaphoreKey() {
+    return "batch-" + getProject().getKey();
+  }
+
+  private Project getProject() {
+    return projectTree.getRootProject();
+  }
+
+  private boolean isInDryRunMode() {
+    return settings.getBoolean(CoreProperties.DRY_RUN);
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java
new file mode 100644 (file)
index 0000000..7519bdc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.scan;
+
+import org.sonar.api.batch.bootstrap.ProjectBuilder;
+
+/**
+ * Barrier to control the project lifecycle :
+ * <p/>
+ * <ul>
+ * <li>initialize the project configuration by executing ProjectBuilder extensions</li>
+ * <li>apply sub-project exclusions (sonar.skippedModules, ...)</li>
+ * <li>---- this barrier ----</li>
+ * <li>run optional dry run database</li>
+ * <li>connect to dry-run or remote database</li>
+ * </ul>
+ */
+public class ProjectReactorReady {
+
+  public ProjectReactorReady(ProjectExclusions exclusions, ProjectBuilder[] projectBuilders) {
+  }
+
+  public ProjectReactorReady(ProjectExclusions exclusions) {
+  }
+
+  public void start() {
+
+  }
+}
index ca518e1eafb83e274cf14df0c9b487b90858df9b..74eaedaab4affe8b779b043595b9d1774fd42163 100644 (file)
@@ -30,9 +30,6 @@ import org.sonar.batch.ProjectTree;
 import org.sonar.batch.bootstrap.ExtensionInstaller;
 import org.sonar.batch.bootstrap.ExtensionUtils;
 import org.sonar.batch.bootstrap.MetricProvider;
-import org.sonar.batch.bootstrap.ProjectExclusions;
-import org.sonar.batch.bootstrap.ProjectLock;
-import org.sonar.batch.bootstrap.ProjectReactorReady;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.DefaultPersistenceManager;
 import org.sonar.batch.index.DefaultResourcePersister;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java b/sonar-batch/src/main/java/org/sonar/batch/scan/UnsupportedProperties.java
new file mode 100644 (file)
index 0000000..46f0e86
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.scan;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.config.Settings;
+
+public class UnsupportedProperties implements BatchComponent {
+  private final Settings settings;
+
+  public UnsupportedProperties(Settings settings) {
+    this.settings = settings;
+  }
+
+  public void start() {
+    verify("sonar.light", "The property 'sonar.light' is no longer supported. Please use 'sonar.dynamicAnalysis'");
+  }
+
+  private void verify(String key, String message) {
+    if (settings.hasKey(key)) {
+      throw new IllegalArgumentException(message);
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/scan/package-info.java
new file mode 100644 (file)
index 0000000..461b7cf
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.scan;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DurationLabelTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DurationLabelTest.java
deleted file mode 100644 (file)
index 1c35d39..0000000
+++ /dev/null
@@ -1,139 +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.Test;
-
-import java.text.MessageFormat;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class DurationLabelTest {
-
-  private static final long SECOND = 1000; // One second in milliseconds
-  private static final long MINUTE = 60 * SECOND; // One minute in milliseconds
-  private static final long HOUR = 60 * MINUTE; // One hour in milliseconds
-  private static final long DAY = 24 * HOUR; // One day in milliseconds
-  private static final long MONTH = 30 * DAY; // 30 days in milliseconds
-  private static final long YEAR = 365 * DAY; // 365 days in milliseconds
-
-  @Test
-  public void testAgoSeconds() {
-    DurationLabel durationLabel = new DurationLabel();
-    String label = durationLabel.label(now() - System.currentTimeMillis());
-    String expected = durationLabel.join(durationLabel.getSeconds(), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoMinute() {
-    DurationLabel durationLabel = new DurationLabel();
-    String label = durationLabel.label(now() - ago(MINUTE));
-    String expected = durationLabel.join(durationLabel.getMinute(), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoMinutes() {
-    DurationLabel durationlabel = new DurationLabel();
-    int minutes = 2;
-    String label = durationlabel.label(now() - ago(minutes * MINUTE));
-    String expected = durationlabel.join(
-        MessageFormat.format(durationlabel.getMinutes(), minutes), durationlabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoHour() {
-    DurationLabel durationLabel = new DurationLabel();
-    String label = durationLabel.label(now() - ago(HOUR));
-    String expected = durationLabel.join(durationLabel.getHour(), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoHours() {
-    DurationLabel durationLabel = new DurationLabel();
-    long hours = 3;
-    String label = durationLabel.label(now() - ago(hours * HOUR));
-    String expected = durationLabel.join(MessageFormat.format(durationLabel.getHours(), hours), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoDay() {
-    DurationLabel durationLabel = new DurationLabel();
-    String label = durationLabel.label(now() - ago(30 * HOUR));
-    String expected = durationLabel.join(durationLabel.getDay(), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoDays() {
-    DurationLabel durationLabel = new DurationLabel();
-    long days = 4;
-    String label = durationLabel.label(now() - ago(days * DAY));
-    String expected = durationLabel.join(MessageFormat.format(durationLabel.getDays(), days), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoMonth() {
-    DurationLabel durationLabel = new DurationLabel();
-    String label = durationLabel.label(now() - ago(35 * DAY));
-    String expected = durationLabel.join(durationLabel.getMonth(), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAgoMonths() {
-    DurationLabel durationLabel = new DurationLabel();
-    long months = 2;
-    String label = durationLabel.label(now() - ago(months * MONTH));
-    String expected = durationLabel.join(MessageFormat.format(durationLabel.getMonths(), months), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testYearAgo() {
-    DurationLabel durationLabel = new DurationLabel();
-    String label = durationLabel.label(now() - ago(14 * MONTH));
-    String expected = durationLabel.join(durationLabel.getYear(), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  @Test
-  public void testYearsAgo() {
-    DurationLabel durationLabel = new DurationLabel();
-    long years = 7;
-    String label = durationLabel.label(now() - ago(years * YEAR));
-    String expected = durationLabel.join(MessageFormat.format(durationLabel.getYears(), years), durationLabel.getSuffixAgo());
-    assertThat(label).isEqualTo(expected);
-  }
-
-  private long ago(long offset) {
-    return System.currentTimeMillis() - offset;
-  }
-
-  private long now() {
-    return System.currentTimeMillis();
-  }
-
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectExclusionsTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectExclusionsTest.java
deleted file mode 100644 (file)
index 3698c1f..0000000
+++ /dev/null
@@ -1,125 +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.Test;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.config.Settings;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-
-public class ProjectExclusionsTest {
-
-  ProjectReactor newReactor(String rootKey, String... moduleKeys) {
-    ProjectDefinition root = ProjectDefinition.create().setKey(rootKey);
-    for (String moduleKey : moduleKeys) {
-      ProjectDefinition module = ProjectDefinition.create().setKey(moduleKey);
-      root.addSubProject(module);
-    }
-    return new ProjectReactor(root);
-  }
-
-  @Test
-  public void testSkippedModules() {
-    Settings settings = new Settings();
-    settings.setProperty("sonar.skippedModules", "sub1,sub3");
-
-    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
-
-    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
-    exclusions.start();
-
-    assertThat(reactor.getProject("root")).isNotNull();
-    assertThat(reactor.getProject("sub1")).isNull();
-    assertThat(reactor.getProject("sub2")).isNotNull();
-  }
-
-  @Test
-  public void testNoSkippedModules() {
-    Settings settings = new Settings();
-    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
-    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
-    exclusions.start();
-
-    assertThat(reactor.getProject("root")).isNotNull();
-    assertThat(reactor.getProject("sub1")).isNotNull();
-    assertThat(reactor.getProject("sub2")).isNotNull();
-  }
-
-  @Test
-  public void testIncludedModules() {
-    Settings settings = new Settings();
-    settings.setProperty("sonar.includedModules", "sub1");
-    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
-    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
-    exclusions.start();
-
-    assertThat(reactor.getProject("root")).isNotNull();
-    assertThat(reactor.getProject("sub1")).isNotNull();
-    assertThat(reactor.getProject("sub2")).isNull();
-  }
-
-  @Test
-  public void shouldBeExcludedIfParentIsExcluded() {
-    ProjectDefinition sub11 = ProjectDefinition.create().setKey("sub11");
-    ProjectDefinition sub1 = ProjectDefinition.create().setKey("sub1").addSubProject(sub11);
-    ProjectDefinition root = ProjectDefinition.create().setKey("root").addSubProject(sub1);
-
-    Settings settings = new Settings();
-    settings.setProperty("sonar.skippedModules", "sub1");
-
-    ProjectReactor reactor = new ProjectReactor(root);
-    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
-    exclusions.start();
-
-
-    assertThat(reactor.getProject("root")).isNotNull();
-    assertThat(reactor.getProject("sub1")).isNull();
-    assertThat(reactor.getProject("sub11")).isNull();
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void shouldFailIfExcludingRoot() {
-    Settings settings = new Settings();
-    settings.setProperty("sonar.skippedModules", "sub1,root");
-
-    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
-    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
-    exclusions.start();
-  }
-
-  @Test
-  public void shouldIgnoreMavenGroupId() {
-    ProjectReactor reactor = newReactor("org.apache.struts:struts", "org.apache.struts:struts-core", "org.apache.struts:struts-taglib");
-
-    Settings settings = new Settings();
-    settings.setProperty("sonar.skippedModules", "struts-taglib");
-
-    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
-    exclusions.start();
-
-
-    assertThat(reactor.getProject("org.apache.struts:struts")).isNotNull();
-    assertThat(reactor.getProject("org.apache.struts:struts-core")).isNotNull();
-    assertThat(reactor.getProject("org.apache.struts:struts-taglib")).isNull();
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java
deleted file mode 100644 (file)
index 9172cd2..0000000
+++ /dev/null
@@ -1,100 +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.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.Semaphores;
-import org.sonar.api.utils.SonarException;
-import org.sonar.batch.ProjectTree;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class ProjectLockTest {
-
-  private ProjectLock projectLock;
-
-  private Semaphores semaphores;
-  private ProjectTree projectTree;
-  private Settings settings;
-
-  private Project project;
-
-  @Before
-  public void setUp() {
-    semaphores = mock(Semaphores.class);
-
-    projectTree = mock(ProjectTree.class);
-    settings = new Settings();
-    setDryRunMode(false);
-    project = new Project("my-project-key");
-    when(projectTree.getRootProject()).thenReturn(project);
-
-    projectLock = new ProjectLock(semaphores, projectTree, settings);
-  }
-
-  @Test
-  public void shouldAcquireSemaphore() {
-    when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(true));
-    projectLock.start();
-
-    verify(semaphores).acquire("batch-my-project-key", 15, 10);
-  }
-
-  @Test(expected = SonarException.class)
-  public void shouldNotAcquireSemaphoreIfTheProjectIsAlreadyBeenAnalysing() {
-    when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(false).setDurationSinceLocked(1234L));
-    projectLock.start();
-  }
-
-  @Test
-  public void shouldNotAcquireSemaphoreInDryRunMode() {
-    setDryRunMode(true);
-    settings = new Settings().setProperty(CoreProperties.DRY_RUN, true);
-    projectLock.start();
-    verifyZeroInteractions(semaphores);
-  }
-
-  @Test
-  public void shouldReleaseSemaphore() {
-    projectLock.stop();
-    verify(semaphores).release("batch-my-project-key");
-  }
-
-  @Test
-  public void shouldNotReleaseSemaphoreInDryRunMode() {
-    setDryRunMode(true);
-    projectLock.stop();
-    verifyZeroInteractions(semaphores);
-  }
-
-  private void setDryRunMode(boolean isInDryRunMode) {
-    settings.setProperty(CoreProperties.DRY_RUN, isInDryRunMode);
-  }
-
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectReactorReadyTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectReactorReadyTest.java
deleted file mode 100644 (file)
index df15f27..0000000
+++ /dev/null
@@ -1,40 +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.Test;
-import org.sonar.api.batch.bootstrap.ProjectBuilder;
-
-import static org.mockito.Mockito.mock;
-
-public class ProjectReactorReadyTest {
-  @Test
-  public void should_do_nothing() {
-    // it's only a barrier
-    ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), new ProjectBuilder[]{mock(ProjectBuilder.class)});
-    barrier.start();
-  }
-
-  @Test
-  public void project_builders_should_be_optional() {
-    ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class));
-    barrier.start();
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectSettingsTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectSettingsTest.java
deleted file mode 100644 (file)
index 5798595..0000000
+++ /dev/null
@@ -1,47 +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.hamcrest.core.Is;
-import org.junit.Test;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.batch.bootstrap.ProjectSettings;
-
-import java.util.List;
-
-import static org.junit.Assert.assertThat;
-
-public class ProjectSettingsTest {
-
-  @Test
-  public void testOrderedProjects() {
-    ProjectDefinition grandParent = ProjectDefinition.create();
-    ProjectDefinition parent = ProjectDefinition.create();
-    ProjectDefinition child = ProjectDefinition.create();
-    grandParent.addSubProject(parent);
-    parent.addSubProject(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));
-
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/UnsupportedPropertiesTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/UnsupportedPropertiesTest.java
deleted file mode 100644 (file)
index 0b8d35d..0000000
+++ /dev/null
@@ -1,47 +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.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.Settings;
-import org.sonar.batch.bootstrap.UnsupportedProperties;
-
-public class UnsupportedPropertiesTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  @Test
-  public void should_fail_if_sonar_light_is_set() {
-    Settings settings = new Settings();
-    settings.setProperty("sonar.light", true);
-
-    thrown.expect(IllegalArgumentException.class);
-    new UnsupportedProperties(settings).start();
-  }
-
-  @Test
-  public void should_not_fail_if_sonar_light_is_not_set() {
-    Settings settings = new Settings();
-    new UnsupportedProperties(settings).start();
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/DurationLabelTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/DurationLabelTest.java
new file mode 100644 (file)
index 0000000..c4b6352
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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.scan;
+
+import org.junit.Test;
+import org.sonar.batch.scan.DurationLabel;
+
+import java.text.MessageFormat;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DurationLabelTest {
+
+  // One second in milliseconds
+  private static final long SECOND = 1000;
+
+  // One minute in milliseconds
+  private static final long MINUTE = 60 * SECOND;
+
+  // One hour in milliseconds
+  private static final long HOUR = 60 * MINUTE;
+
+  // One day in milliseconds
+  private static final long DAY = 24 * HOUR;
+
+  // 30 days in milliseconds
+  private static final long MONTH = 30 * DAY;
+
+  // 365 days in milliseconds
+  private static final long YEAR = 365 * DAY;
+
+  @Test
+  public void testAgoSeconds() {
+    DurationLabel durationLabel = new DurationLabel();
+    String label = durationLabel.label(now() - System.currentTimeMillis());
+    String expected = durationLabel.join(durationLabel.getSeconds(), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoMinute() {
+    DurationLabel durationLabel = new DurationLabel();
+    String label = durationLabel.label(now() - ago(MINUTE));
+    String expected = durationLabel.join(durationLabel.getMinute(), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoMinutes() {
+    DurationLabel durationlabel = new DurationLabel();
+    int minutes = 2;
+    String label = durationlabel.label(now() - ago(minutes * MINUTE));
+    String expected = durationlabel.join(
+        MessageFormat.format(durationlabel.getMinutes(), minutes), durationlabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoHour() {
+    DurationLabel durationLabel = new DurationLabel();
+    String label = durationLabel.label(now() - ago(HOUR));
+    String expected = durationLabel.join(durationLabel.getHour(), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoHours() {
+    DurationLabel durationLabel = new DurationLabel();
+    long hours = 3;
+    String label = durationLabel.label(now() - ago(hours * HOUR));
+    String expected = durationLabel.join(MessageFormat.format(durationLabel.getHours(), hours), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoDay() {
+    DurationLabel durationLabel = new DurationLabel();
+    String label = durationLabel.label(now() - ago(30 * HOUR));
+    String expected = durationLabel.join(durationLabel.getDay(), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoDays() {
+    DurationLabel durationLabel = new DurationLabel();
+    long days = 4;
+    String label = durationLabel.label(now() - ago(days * DAY));
+    String expected = durationLabel.join(MessageFormat.format(durationLabel.getDays(), days), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoMonth() {
+    DurationLabel durationLabel = new DurationLabel();
+    String label = durationLabel.label(now() - ago(35 * DAY));
+    String expected = durationLabel.join(durationLabel.getMonth(), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testAgoMonths() {
+    DurationLabel durationLabel = new DurationLabel();
+    long months = 2;
+    String label = durationLabel.label(now() - ago(months * MONTH));
+    String expected = durationLabel.join(MessageFormat.format(durationLabel.getMonths(), months), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testYearAgo() {
+    DurationLabel durationLabel = new DurationLabel();
+    String label = durationLabel.label(now() - ago(14 * MONTH));
+    String expected = durationLabel.join(durationLabel.getYear(), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  @Test
+  public void testYearsAgo() {
+    DurationLabel durationLabel = new DurationLabel();
+    long years = 7;
+    String label = durationLabel.label(now() - ago(years * YEAR));
+    String expected = durationLabel.join(MessageFormat.format(durationLabel.getYears(), years), durationLabel.getSuffixAgo());
+    assertThat(label).isEqualTo(expected);
+  }
+
+  private long ago(long offset) {
+    return System.currentTimeMillis() - offset;
+  }
+
+  private long now() {
+    return System.currentTimeMillis();
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
new file mode 100644 (file)
index 0000000..010cb8a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.scan;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.junit.Test;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.batch.bootstrap.BatchSettings;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ModuleSettingsTest {
+
+  @Test
+  public void testOrderedProjects() {
+    ProjectDefinition grandParent = ProjectDefinition.create();
+    ProjectDefinition parent = ProjectDefinition.create();
+    ProjectDefinition child = ProjectDefinition.create();
+    grandParent.addSubProject(parent);
+    parent.addSubProject(child);
+
+    List<ProjectDefinition> hierarchy = ModuleSettings.getTopDownParentProjects(child);
+    assertThat(hierarchy.get(0)).isEqualTo(grandParent);
+    assertThat(hierarchy.get(1)).isEqualTo(parent);
+    assertThat(hierarchy.get(2)).isEqualTo(child);
+  }
+
+  @Test
+  public void test_loading_of_module_settings() {
+    BatchSettings batchSettings = mock(BatchSettings.class);
+    when(batchSettings.getDefinitions()).thenReturn(new PropertyDefinitions());
+    when(batchSettings.getProperties()).thenReturn(ImmutableMap.of(
+      "overridding", "batch",
+      "on-batch", "true"
+    ));
+    when(batchSettings.getModuleProperties("struts-core")).thenReturn(ImmutableMap.of(
+      "on-module", "true",
+      "overridding", "module"
+    ));
+
+    ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
+    Configuration deprecatedConf = new PropertiesConfiguration();
+
+    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, deprecatedConf);
+
+    assertThat(moduleSettings.getString("overridding")).isEqualTo("module");
+    assertThat(moduleSettings.getString("on-batch")).isEqualTo("true");
+    assertThat(moduleSettings.getString("on-module")).isEqualTo("true");
+
+    assertThat(deprecatedConf.getString("overridding")).isEqualTo("module");
+    assertThat(deprecatedConf.getString("on-batch")).isEqualTo("true");
+    assertThat(deprecatedConf.getString("on-module")).isEqualTo("true");
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectExclusionsTest.java
new file mode 100644 (file)
index 0000000..8ec0723
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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.scan;
+
+import org.junit.Test;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.scan.ProjectExclusions;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+
+public class ProjectExclusionsTest {
+
+  ProjectReactor newReactor(String rootKey, String... moduleKeys) {
+    ProjectDefinition root = ProjectDefinition.create().setKey(rootKey);
+    for (String moduleKey : moduleKeys) {
+      ProjectDefinition module = ProjectDefinition.create().setKey(moduleKey);
+      root.addSubProject(module);
+    }
+    return new ProjectReactor(root);
+  }
+
+  @Test
+  public void testSkippedModules() {
+    Settings settings = new Settings();
+    settings.setProperty("sonar.skippedModules", "sub1,sub3");
+
+    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+
+    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
+    exclusions.start();
+
+    assertThat(reactor.getProject("root")).isNotNull();
+    assertThat(reactor.getProject("sub1")).isNull();
+    assertThat(reactor.getProject("sub2")).isNotNull();
+  }
+
+  @Test
+  public void testNoSkippedModules() {
+    Settings settings = new Settings();
+    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
+    exclusions.start();
+
+    assertThat(reactor.getProject("root")).isNotNull();
+    assertThat(reactor.getProject("sub1")).isNotNull();
+    assertThat(reactor.getProject("sub2")).isNotNull();
+  }
+
+  @Test
+  public void testIncludedModules() {
+    Settings settings = new Settings();
+    settings.setProperty("sonar.includedModules", "sub1");
+    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
+    exclusions.start();
+
+    assertThat(reactor.getProject("root")).isNotNull();
+    assertThat(reactor.getProject("sub1")).isNotNull();
+    assertThat(reactor.getProject("sub2")).isNull();
+  }
+
+  @Test
+  public void shouldBeExcludedIfParentIsExcluded() {
+    ProjectDefinition sub11 = ProjectDefinition.create().setKey("sub11");
+    ProjectDefinition sub1 = ProjectDefinition.create().setKey("sub1").addSubProject(sub11);
+    ProjectDefinition root = ProjectDefinition.create().setKey("root").addSubProject(sub1);
+
+    Settings settings = new Settings();
+    settings.setProperty("sonar.skippedModules", "sub1");
+
+    ProjectReactor reactor = new ProjectReactor(root);
+    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
+    exclusions.start();
+
+
+    assertThat(reactor.getProject("root")).isNotNull();
+    assertThat(reactor.getProject("sub1")).isNull();
+    assertThat(reactor.getProject("sub11")).isNull();
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void shouldFailIfExcludingRoot() {
+    Settings settings = new Settings();
+    settings.setProperty("sonar.skippedModules", "sub1,root");
+
+    ProjectReactor reactor = newReactor("root", "sub1", "sub2");
+    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
+    exclusions.start();
+  }
+
+  @Test
+  public void shouldIgnoreMavenGroupId() {
+    ProjectReactor reactor = newReactor("org.apache.struts:struts", "org.apache.struts:struts-core", "org.apache.struts:struts-taglib");
+
+    Settings settings = new Settings();
+    settings.setProperty("sonar.skippedModules", "struts-taglib");
+
+    ProjectExclusions exclusions = new ProjectExclusions(settings, reactor);
+    exclusions.start();
+
+
+    assertThat(reactor.getProject("org.apache.struts:struts")).isNotNull();
+    assertThat(reactor.getProject("org.apache.struts:struts-core")).isNotNull();
+    assertThat(reactor.getProject("org.apache.struts:struts-taglib")).isNull();
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java
new file mode 100644 (file)
index 0000000..708b6ef
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.scan;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.Semaphores;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.ProjectTree;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class ProjectLockTest {
+
+  ProjectLock projectLock;
+  Semaphores semaphores = mock(Semaphores.class);
+  ProjectTree projectTree = mock(ProjectTree.class);
+  Settings settings;
+  Project project;
+
+  @Before
+  public void setUp() {
+    settings = new Settings();
+    setDryRunMode(false);
+    project = new Project("my-project-key");
+    when(projectTree.getRootProject()).thenReturn(project);
+
+    projectLock = new ProjectLock(semaphores, projectTree, settings);
+  }
+
+  @Test
+  public void shouldAcquireSemaphore() {
+    when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(true));
+    projectLock.start();
+
+    verify(semaphores).acquire("batch-my-project-key", 15, 10);
+  }
+
+  @Test(expected = SonarException.class)
+  public void shouldNotAcquireSemaphoreIfTheProjectIsAlreadyBeenAnalysing() {
+    when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(false).setDurationSinceLocked(1234L));
+    projectLock.start();
+  }
+
+  @Test
+  public void shouldNotAcquireSemaphoreInDryRunMode() {
+    setDryRunMode(true);
+    settings = new Settings().setProperty(CoreProperties.DRY_RUN, true);
+    projectLock.start();
+    verifyZeroInteractions(semaphores);
+  }
+
+  @Test
+  public void shouldReleaseSemaphore() {
+    projectLock.stop();
+    verify(semaphores).release("batch-my-project-key");
+  }
+
+  @Test
+  public void shouldNotReleaseSemaphoreInDryRunMode() {
+    setDryRunMode(true);
+    projectLock.stop();
+    verifyZeroInteractions(semaphores);
+  }
+
+  private void setDryRunMode(boolean isInDryRunMode) {
+    settings.setProperty(CoreProperties.DRY_RUN, isInDryRunMode);
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java
new file mode 100644 (file)
index 0000000..55ddf6b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.scan;
+
+import org.junit.Test;
+import org.sonar.api.batch.bootstrap.ProjectBuilder;
+
+import static org.mockito.Mockito.mock;
+
+public class ProjectReactorReadyTest {
+  @Test
+  public void should_do_nothing() {
+    // it's only a barrier
+    ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), new ProjectBuilder[]{mock(ProjectBuilder.class)});
+    barrier.start();
+  }
+
+  @Test
+  public void project_builders_should_be_optional() {
+    ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class));
+    barrier.start();
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/UnsupportedPropertiesTest.java
new file mode 100644 (file)
index 0000000..67fe9c8
--- /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.scan;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.scan.UnsupportedProperties;
+
+public class UnsupportedPropertiesTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void should_fail_if_sonar_light_is_set() {
+    Settings settings = new Settings();
+    settings.setProperty("sonar.light", true);
+
+    thrown.expect(IllegalArgumentException.class);
+    new UnsupportedProperties(settings).start();
+  }
+
+  @Test
+  public void should_not_fail_if_sonar_light_is_not_set() {
+    Settings settings = new Settings();
+    new UnsupportedProperties(settings).start();
+  }
+}
index 92520a2e699081064f342574dfb0ba431ed32ff5..72e2a84b807ff2aa8b6312a8380c5b152571758f 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.config;
 import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.ArrayUtils;
@@ -50,9 +51,9 @@ import java.util.Properties;
  */
 public class Settings implements BatchComponent, ServerComponent {
 
-  protected final Map<String, String> properties;
-  protected final PropertyDefinitions definitions;
-  private final Encryption encryption;
+  protected Map<String, String> properties;
+  protected PropertyDefinitions definitions;
+  private Encryption encryption;
 
   public Settings() {
     this(new PropertyDefinitions());
@@ -75,23 +76,23 @@ public class Settings implements BatchComponent, ServerComponent {
     this.encryption = other.encryption;
   }
 
-  public final Encryption getEncryption() {
+  public Encryption getEncryption() {
     return encryption;
   }
 
-  public final String getDefaultValue(String key) {
+  public String getDefaultValue(String key) {
     return definitions.getDefaultValue(key);
   }
 
-  public final boolean hasKey(String key) {
+  public boolean hasKey(String key) {
     return properties.containsKey(key);
   }
 
-  public final boolean hasDefaultValue(String key) {
+  public boolean hasDefaultValue(String key) {
     return StringUtils.isNotEmpty(getDefaultValue(key));
   }
 
-  public final String getString(String key) {
+  public String getString(String key) {
     String value = getClearString(key);
     if (value != null && encryption.isEncrypted(value)) {
       try {
@@ -115,7 +116,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return value;
   }
 
-  public final boolean getBoolean(String key) {
+  public boolean getBoolean(String key) {
     String value = getString(key);
     return StringUtils.isNotEmpty(value) && Boolean.parseBoolean(value);
   }
@@ -123,7 +124,7 @@ public class Settings implements BatchComponent, ServerComponent {
   /**
    * @return the value as int. If the property does not exist and has no default value, then 0 is returned.
    */
-  public final int getInt(String key) {
+  public int getInt(String key) {
     String value = getString(key);
     if (StringUtils.isNotEmpty(value)) {
       return Integer.parseInt(value);
@@ -131,7 +132,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return 0;
   }
 
-  public final long getLong(String key) {
+  public long getLong(String key) {
     String value = getString(key);
     if (StringUtils.isNotEmpty(value)) {
       return Long.parseLong(value);
@@ -139,7 +140,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return 0L;
   }
 
-  public final Date getDate(String key) {
+  public Date getDate(String key) {
     String value = getString(key);
     if (StringUtils.isNotEmpty(value)) {
       return DateUtils.parseDate(value);
@@ -147,7 +148,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return null;
   }
 
-  public final Date getDateTime(String key) {
+  public Date getDateTime(String key) {
     String value = getString(key);
     if (StringUtils.isNotEmpty(value)) {
       return DateUtils.parseDateTime(value);
@@ -155,7 +156,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return null;
   }
 
-  public final Float getFloat(String key) {
+  public Float getFloat(String key) {
     String value = getString(key);
     if (StringUtils.isNotEmpty(value)) {
       try {
@@ -167,7 +168,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return null;
   }
 
-  public final Double getDouble(String key) {
+  public Double getDouble(String key) {
     String value = getString(key);
     if (StringUtils.isNotEmpty(value)) {
       try {
@@ -189,7 +190,7 @@ public class Settings implements BatchComponent, ServerComponent {
    * <li>"one, , three" -> ["one", "", "three"]</li>
    * </ul>
    */
-  public final String[] getStringArray(String key) {
+  public String[] getStringArray(String key) {
     PropertyDefinition property = getDefinitions().get(key);
     if ((null != property) && (property.isMultiValues())) {
       String value = getString(key);
@@ -213,7 +214,7 @@ public class Settings implements BatchComponent, ServerComponent {
    * @return non-null array of lines. The line termination characters are excluded.
    * @since 3.2
    */
-  public final String[] getStringLines(String key) {
+  public String[] getStringLines(String key) {
     String value = getString(key);
     if (Strings.isNullOrEmpty(value)) {
       return ArrayUtils.EMPTY_STRING_ARRAY;
@@ -224,7 +225,7 @@ public class Settings implements BatchComponent, ServerComponent {
   /**
    * Value is splitted and trimmed.
    */
-  public final String[] getStringArrayBySeparator(String key, String separator) {
+  public String[] getStringArrayBySeparator(String key, String separator) {
     String value = getString(key);
     if (value != null) {
       String[] strings = StringUtils.splitByWholeSeparator(value, separator);
@@ -237,7 +238,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return ArrayUtils.EMPTY_STRING_ARRAY;
   }
 
-  public final List<String> getKeysStartingWith(String prefix) {
+  public List<String> getKeysStartingWith(String prefix) {
     List<String> result = Lists.newArrayList();
     for (String key : properties.keySet()) {
       if (StringUtils.startsWith(key, prefix)) {
@@ -247,7 +248,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return result;
   }
 
-  public final Settings appendProperty(String key, String value) {
+  public Settings appendProperty(String key, String value) {
     String newValue = properties.get(definitions.validKey(key));
     if (StringUtils.isEmpty(newValue)) {
       newValue = StringUtils.trim(value);
@@ -257,7 +258,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return setProperty(key, newValue);
   }
 
-  public final Settings setProperty(String key, @Nullable String[] values) {
+  public Settings setProperty(String key, @Nullable String[] values) {
     PropertyDefinition property = getDefinitions().get(key);
     if ((null == property) || (!property.isMultiValues())) {
       throw new IllegalStateException("Fail to set multiple values on a single value property " + key);
@@ -280,7 +281,7 @@ public class Settings implements BatchComponent, ServerComponent {
     return setProperty(key, text);
   }
 
-  public final Settings setProperty(String key, @Nullable String value) {
+  public Settings setProperty(String key, @Nullable String value) {
     String validKey = definitions.validKey(key);
     if (value == null) {
       properties.remove(validKey);
@@ -292,79 +293,79 @@ public class Settings implements BatchComponent, ServerComponent {
     return this;
   }
 
-  public final Settings setProperty(String key, @Nullable Boolean value) {
+  public Settings setProperty(String key, @Nullable Boolean value) {
     return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
-  public final Settings setProperty(String key, @Nullable Integer value) {
+  public Settings setProperty(String key, @Nullable Integer value) {
     return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
-  public final Settings setProperty(String key, @Nullable Long value) {
+  public Settings setProperty(String key, @Nullable Long value) {
     return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
-  public final Settings setProperty(String key, @Nullable Double value) {
+  public Settings setProperty(String key, @Nullable Double value) {
     return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
-  public final Settings setProperty(String key, @Nullable Float value) {
+  public Settings setProperty(String key, @Nullable Float value) {
     return setProperty(key, value == null ? null : String.valueOf(value));
   }
 
-  public final Settings setProperty(String key, @Nullable Date date) {
+  public Settings setProperty(String key, @Nullable Date date) {
     return setProperty(key, date, false);
   }
 
-  public final Settings addProperties(Map<String, String> props) {
+  public Settings addProperties(Map<String, String> props) {
     for (Map.Entry<String, String> entry : props.entrySet()) {
       setProperty(entry.getKey(), entry.getValue());
     }
     return this;
   }
 
-  public final Settings addProperties(Properties props) {
+  public Settings addProperties(Properties props) {
     for (Map.Entry<Object, Object> entry : props.entrySet()) {
       setProperty(entry.getKey().toString(), entry.getValue().toString());
     }
     return this;
   }
 
-  public final Settings addSystemProperties() {
+  public Settings addSystemProperties() {
     return addProperties(System.getProperties());
   }
 
-  public final Settings addEnvironmentVariables() {
+  public Settings addEnvironmentVariables() {
     return addProperties(System.getenv());
   }
 
-  public final Settings setProperties(Map<String, String> props) {
+  public Settings setProperties(Map<String, String> props) {
     clear();
     return addProperties(props);
   }
 
-  public final Settings setProperty(String key, @Nullable Date date, boolean includeTime) {
+  public Settings setProperty(String key, @Nullable Date date, boolean includeTime) {
     return setProperty(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date));
   }
 
-  public final Settings removeProperty(String key) {
+  public Settings removeProperty(String key) {
     return setProperty(key, (String) null);
   }
 
-  public final Settings clear() {
+  public Settings clear() {
     properties.clear();
     doOnClearProperties();
     return this;
   }
 
   /**
-   * @return unmodifiable properties
+   * @return immutable properties
    */
-  public final Map<String, String> getProperties() {
-    return Collections.unmodifiableMap(properties);
+  public Map<String, String> getProperties() {
+    return ImmutableMap.copyOf(properties);
   }
 
-  public final PropertyDefinitions getDefinitions() {
+  public PropertyDefinitions getDefinitions() {
     return definitions;
   }