]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4969 Do not mix batch and server classpaths
authorSimon Brandhof <simon.brandhof@gmail.com>
Thu, 17 Apr 2014 21:33:27 +0000 (23:33 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Thu, 17 Apr 2014 21:33:49 +0000 (23:33 +0200)
97 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java [deleted file]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilter.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java [deleted file]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewDebtDecorator.java [deleted file]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/package-info.java [deleted file]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/ExclusionPropertiesTest.java [deleted file]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilterTest.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java [deleted file]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewDebtDecoratorTest.java [deleted file]
plugins/sonar-maven-batch-plugin/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/DefaultMavenPluginExecutor.java [deleted file]
plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenBatchPlugin.java [deleted file]
plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectBootstrapper.java [deleted file]
plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectBuilder.java [deleted file]
plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java [deleted file]
plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/package-info.java [deleted file]
plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/DefaultMavenPluginExecutorTest.java [deleted file]
plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenBatchPluginTest.java [deleted file]
plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenProjectBootstrapperTest.java [deleted file]
plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenProjectConverterTest.java [deleted file]
plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/SonarMavenProjectBuilderTest.java [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinks/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml [deleted file]
plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/sourceEncoding/pom.xml [deleted file]
pom.xml
sonar-application/assembly.xml
sonar-application/pom.xml
sonar-batch-maven-compat/pom.xml
sonar-batch/pom.xml
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java
sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/issue/ignore/IssueExclusionsConfiguration.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/issue/ignore/pattern/AbstractPatternInitializer.java
sonar-batch/src/main/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializer.java
sonar-batch/src/main/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializer.java
sonar-batch/src/main/java/org/sonar/batch/maven/DefaultMavenPluginExecutor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectBootstrapper.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectBuilder.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectConverter.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/maven/package-info.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/ProjectScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/debt/NewDebtDecoratorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/issue/ignore/IssueExclusionsConfigurationTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/ignore/pattern/IssueInclusionPatternInitializerTest.java
sonar-batch/src/test/java/org/sonar/batch/maven/DefaultMavenPluginExecutorTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/maven/MavenProjectBootstrapperTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/maven/MavenProjectConverterTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/maven/SonarMavenProjectBuilderTest.java [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinks/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/sourceEncoding/pom.xml [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/config/ExclusionProperties.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/config/IssueExclusionProperties.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/config/SecurityProperties.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java [new file with mode: 0644]
sonar-server/pom.xml
sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java [deleted file]
sonar-server/src/main/java/org/sonar/server/plugins/BatchWs.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/startup/GenerateBootstrapIndex.java [deleted file]
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/batch_bootstrap_controller.rb
sonar-server/src/main/webapp/WEB-INF/config/routes.rb
sonar-server/src/main/webapp/WEB-INF/web.xml
sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java [deleted file]
sonar-server/src/test/java/org/sonar/server/plugins/BatchWsTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/startup/GenerateBootstrapIndexTest.java [deleted file]

index a87d01690a692a5c8a4eaef16c926ffb96304c37..b8770197f47d2ae6f6d7bb173176f6d47ab9c25c 100644 (file)
 package org.sonar.plugins.core;
 
 import com.google.common.collect.ImmutableList;
-import org.sonar.api.*;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.Properties;
+import org.sonar.api.Property;
+import org.sonar.api.PropertyType;
+import org.sonar.api.SonarPlugin;
 import org.sonar.api.checks.NoSonarFilter;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.batch.components.PastSnapshotFinder;
-import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.plugins.core.batch.IndexProjectPostJob;
 import org.sonar.plugins.core.charts.DistributionAreaChart;
 import org.sonar.plugins.core.charts.DistributionBarChart;
 import org.sonar.plugins.core.charts.XradarChart;
 import org.sonar.plugins.core.colorizers.JavaColorizerFormat;
-import org.sonar.plugins.core.dashboards.*;
-import org.sonar.plugins.core.issue.*;
-import org.sonar.plugins.core.issue.notification.*;
+import org.sonar.plugins.core.dashboards.GlobalDefaultDashboard;
+import org.sonar.plugins.core.dashboards.ProjectDefaultDashboard;
+import org.sonar.plugins.core.dashboards.ProjectHotspotDashboard;
+import org.sonar.plugins.core.dashboards.ProjectIssuesDashboard;
+import org.sonar.plugins.core.dashboards.ProjectTimeMachineDashboard;
+import org.sonar.plugins.core.issue.CountFalsePositivesDecorator;
+import org.sonar.plugins.core.issue.CountUnresolvedIssuesDecorator;
+import org.sonar.plugins.core.issue.InitialOpenIssuesSensor;
+import org.sonar.plugins.core.issue.InitialOpenIssuesStack;
+import org.sonar.plugins.core.issue.IssueHandlers;
+import org.sonar.plugins.core.issue.IssueTracking;
+import org.sonar.plugins.core.issue.IssueTrackingDecorator;
+import org.sonar.plugins.core.issue.IssuesDensityDecorator;
+import org.sonar.plugins.core.issue.WeightedIssuesDecorator;
+import org.sonar.plugins.core.issue.notification.ChangesOnMyIssueNotificationDispatcher;
+import org.sonar.plugins.core.issue.notification.IssueChangesEmailTemplate;
+import org.sonar.plugins.core.issue.notification.NewFalsePositiveNotificationDispatcher;
+import org.sonar.plugins.core.issue.notification.NewIssuesEmailTemplate;
+import org.sonar.plugins.core.issue.notification.NewIssuesNotificationDispatcher;
+import org.sonar.plugins.core.issue.notification.SendIssueNotificationsPostJob;
 import org.sonar.plugins.core.measurefilters.MyFavouritesFilter;
 import org.sonar.plugins.core.measurefilters.ProjectFilter;
 import org.sonar.plugins.core.notifications.alerts.NewAlerts;
 import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
-import org.sonar.plugins.core.sensors.*;
-import org.sonar.plugins.core.technicaldebt.DebtDecorator;
-import org.sonar.plugins.core.technicaldebt.NewDebtDecorator;
-import org.sonar.plugins.core.timemachine.*;
+import org.sonar.plugins.core.sensors.BranchCoverageDecorator;
+import org.sonar.plugins.core.sensors.CommentDensityDecorator;
+import org.sonar.plugins.core.sensors.CoverageDecorator;
+import org.sonar.plugins.core.sensors.CoverageMeasurementFilter;
+import org.sonar.plugins.core.sensors.DirectoriesDecorator;
+import org.sonar.plugins.core.sensors.FileHashSensor;
+import org.sonar.plugins.core.sensors.FilesDecorator;
+import org.sonar.plugins.core.sensors.ItBranchCoverageDecorator;
+import org.sonar.plugins.core.sensors.ItCoverageDecorator;
+import org.sonar.plugins.core.sensors.ItLineCoverageDecorator;
+import org.sonar.plugins.core.sensors.LineCoverageDecorator;
+import org.sonar.plugins.core.sensors.ManualMeasureDecorator;
+import org.sonar.plugins.core.sensors.OverallBranchCoverageDecorator;
+import org.sonar.plugins.core.sensors.OverallCoverageDecorator;
+import org.sonar.plugins.core.sensors.OverallLineCoverageDecorator;
+import org.sonar.plugins.core.sensors.ProfileEventsSensor;
+import org.sonar.plugins.core.sensors.ProjectLinksSensor;
+import org.sonar.plugins.core.sensors.UnitTestDecorator;
+import org.sonar.plugins.core.sensors.VersionEventsSensor;
+import org.sonar.plugins.core.timemachine.NewCoverageAggregator;
+import org.sonar.plugins.core.timemachine.NewCoverageFileAnalyzer;
+import org.sonar.plugins.core.timemachine.NewItCoverageFileAnalyzer;
+import org.sonar.plugins.core.timemachine.NewOverallCoverageFileAnalyzer;
+import org.sonar.plugins.core.timemachine.TendencyDecorator;
+import org.sonar.plugins.core.timemachine.TimeMachineConfigurationPersister;
+import org.sonar.plugins.core.timemachine.VariationDecorator;
 import org.sonar.plugins.core.web.TestsViewer;
 import org.sonar.plugins.core.widgets.*;
-import org.sonar.plugins.core.widgets.issues.*;
-import org.sonar.plugins.core.widgets.measures.*;
+import org.sonar.plugins.core.widgets.issues.ActionPlansWidget;
+import org.sonar.plugins.core.widgets.issues.FalsePositiveIssuesWidget;
+import org.sonar.plugins.core.widgets.issues.IssueFilterWidget;
+import org.sonar.plugins.core.widgets.issues.IssuesWidget;
+import org.sonar.plugins.core.widgets.issues.MyUnresolvedIssuesWidget;
+import org.sonar.plugins.core.widgets.issues.UnresolvedIssuesPerAssigneeWidget;
+import org.sonar.plugins.core.widgets.issues.UnresolvedIssuesStatusesWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsBubbleChartWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsCloudWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsHistogramWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsPieChartWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterListWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterTreemapWidget;
 
-import java.util.Arrays;
 import java.util.List;
 
 @Properties({
@@ -292,11 +340,6 @@ public final class CorePlugin extends SonarPlugin {
       NewFalsePositiveNotificationDispatcher.class,
       NewFalsePositiveNotificationDispatcher.newMetadata(),
 
-      // technical debt
-      DebtDecorator.class,
-      NewDebtDecorator.class,
-      IssueChangelogDebtCalculator.class,
-
       // batch
       ProfileEventsSensor.class,
       ProjectLinksSensor.class,
@@ -334,64 +377,7 @@ public final class CorePlugin extends SonarPlugin {
       NewAlerts.class,
       NewAlerts.newMetadata());
 
-    extensions.addAll(ExclusionProperties.definitions());
-    extensions.addAll(IssueExclusionsConfiguration.getPropertyDefinitions());
-    extensions.addAll(CoverageMeasurementFilter.getPropertyDefinitions());
-    extensions.addAll(PastSnapshotFinder.getPropertyDefinitions());
-    extensions.addAll(DebtDecorator.definitions());
-    extensions.addAll(propertyDefinitions());
-
     return extensions.build();
   }
 
-  static List<PropertyDefinition> propertyDefinitions() {
-    return Arrays.asList(
-      PropertyDefinition.builder(CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY)
-        .defaultValue("en")
-        .name("Locale used for issue messages")
-        .description("Deprecated property. Keep default value for backward compatibility.")
-        .hidden()
-        .build(),
-      PropertyDefinition.builder(CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY)
-        .defaultValue("" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE)
-        .name("Allow users to sign up online")
-        .description("Users can sign up online.")
-        .type(PropertyType.BOOLEAN)
-        .category(CoreProperties.CATEGORY_SECURITY)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.CORE_DEFAULT_GROUP)
-        .defaultValue(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE)
-        .name("Default user group")
-        .description("Any new users will automatically join this group.")
-        .category(CoreProperties.CATEGORY_SECURITY)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY)
-        .defaultValue("" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE)
-        .name("Import sources")
-        .description("Set to false if sources should not be imported and therefore not available in the Web UI (e.g. for security reasons).")
-        .type(PropertyType.BOOLEAN)
-        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
-        .category(CoreProperties.CATEGORY_SECURITY)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY)
-        .defaultValue("" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE)
-        .name("Force user authentication")
-        .description("Forcing user authentication stops un-logged users to access SonarQube.")
-        .type(PropertyType.BOOLEAN)
-        .category(CoreProperties.CATEGORY_SECURITY)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION)
-        .defaultValue(Boolean.toString(false))
-        .name("Prevent automatic project creation")
-        .description("Set to true to prevent automatic project creation at first analysis and force project provisioning.")
-        .type(PropertyType.BOOLEAN)
-        .category(CoreProperties.CATEGORY_SECURITY)
-        .build()
-    );
-  }
-
 }
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ExclusionProperties.java
deleted file mode 100644 (file)
index b169a57..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.core;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.resources.Qualifiers;
-
-import java.util.List;
-
-class ExclusionProperties {
-
-  private ExclusionProperties() {
-    // only static stuff
-  }
-
-  static List<PropertyDefinition> definitions() {
-    return ImmutableList.of(
-      PropertyDefinition.builder(CoreProperties.PROJECT_INCLUSIONS_PROPERTY)
-        .name("Source File Inclusions")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(3)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY)
-        .name("Test File Inclusions")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(5)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY)
-        .name("Global Source File Exclusions")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .index(0)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY)
-        .name("Global Test File Exclusions")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .index(1)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY)
-        .name("Source File Exclusions")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(2)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY)
-        .name("Test File Exclusions")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(4)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.CORE_SKIPPED_MODULES_PROPERTY)
-        .name("Exclude Modules")
-        .description("Maven artifact ids of modules to exclude.")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .onlyOnQualifiers(Qualifiers.PROJECT)
-        .index(0)
-        .build(),
-      PropertyDefinition.builder(CoreProperties.CORE_INCLUDED_MODULES_PROPERTY)
-        .name("Include Modules")
-        .description("Maven artifact ids of modules to include.")
-        .multiValues(true)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
-        .onlyOnQualifiers(Qualifiers.PROJECT)
-        .index(1)
-        .build());
-  }
-}
index e2bcea80dce9b77af37c8df01ab8926fb757c4b0..eeda60396a926e582ec89132dd22c26c2052eb0d 100644 (file)
@@ -23,34 +23,26 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableSet;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.config.Settings;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.utils.WildcardPattern;
 import org.sonar.core.measure.MeasurementFilter;
 
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 
 public class CoverageMeasurementFilter implements MeasurementFilter {
 
-  public static final String PROPERTY_COVERAGE_EXCLUSIONS = "sonar.coverage.exclusions";
-  public static final String PROPERTY_COVERAGE_INCLUSIONS = "sonar.coverage.inclusions";
-
   private final Settings settings;
   private final ImmutableSet<Metric> coverageMetrics;
   private Collection<WildcardPattern> resourcePatterns;
 
   public CoverageMeasurementFilter(Settings settings,
-    CoverageDecorator coverageDecorator,
-    LineCoverageDecorator lineCoverageDecorator,
-    BranchCoverageDecorator branchCoverageDecorator) {
+                                   CoverageDecorator coverageDecorator,
+                                   LineCoverageDecorator lineCoverageDecorator,
+                                   BranchCoverageDecorator branchCoverageDecorator) {
     this.settings = settings;
     this.coverageMetrics = ImmutableSet.<Metric>builder()
       .addAll(coverageDecorator.generatedMetrics())
@@ -88,22 +80,10 @@ public class CoverageMeasurementFilter implements MeasurementFilter {
 
   @VisibleForTesting
   final void initPatterns() {
-    Builder<WildcardPattern> builder = ImmutableList.<WildcardPattern>builder();
-    for (String pattern : settings.getStringArray(PROPERTY_COVERAGE_EXCLUSIONS)) {
+    Builder<WildcardPattern> builder = ImmutableList.builder();
+    for (String pattern : settings.getStringArray("sonar.coverage.exclusions")) {
       builder.add(WildcardPattern.create(pattern));
     }
     resourcePatterns = builder.build();
   }
-
-  public static List<PropertyDefinition> getPropertyDefinitions() {
-    return ImmutableList.of(
-      PropertyDefinition.builder(PROPERTY_COVERAGE_EXCLUSIONS)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(CoreProperties.SUBCATEGORY_COVERAGE_EXCLUSIONS)
-        .type(PropertyType.STRING)
-        .multiValues(true)
-        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
-        .build()
-      );
-  }
 }
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/DebtDecorator.java
deleted file mode 100644 (file)
index 2caf164..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.plugins.core.technicaldebt;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.batch.*;
-import org.sonar.api.batch.rule.Rule;
-import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.measures.*;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.ResourceUtils;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-
-/**
- * Decorator that computes the technical debt metric
- */
-@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
-public final class DebtDecorator implements Decorator {
-
-  private final ResourcePerspectives perspectives;
-  private final TechnicalDebtModel model;
-  private final Rules rules;
-
-  /**
-   * ruleFinder is needed to load "old" rule in order to persist rule measure
-   */
-  private final RuleFinder ruleFinder;
-
-  public DebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules, RuleFinder ruleFinder) {
-    this.perspectives = perspectives;
-    this.model = model;
-    this.rules = rules;
-    this.ruleFinder = ruleFinder;
-  }
-
-  public boolean shouldExecuteOnProject(Project project) {
-    return true;
-  }
-
-  @DependedUpon
-  public List<Metric> generatesMetrics() {
-    return Arrays.asList(CoreMetrics.TECHNICAL_DEBT);
-  }
-
-  public void decorate(Resource resource, DecoratorContext context) {
-    Issuable issuable = perspectives.as(Issuable.class, resource);
-    if (issuable != null && shouldSaveMeasure(context)) {
-      List<Issue> issues = newArrayList(issuable.issues());
-      saveMeasures(context, issues);
-    }
-  }
-
-  private void saveMeasures(DecoratorContext context, List<Issue> issues) {
-    Long total = 0L;
-    SumMap<RuleKey> ruleDebts = new SumMap<RuleKey>();
-    SumMap<Characteristic> characteristicDebts = new SumMap<Characteristic>();
-
-    // Aggregate rules debt from current issues (and populate current characteristic debt)
-    for (Issue issue : issues) {
-      Long debt = ((DefaultIssue) issue).debtInMinutes();
-      total += computeDebt(debt, issue.ruleKey(), ruleDebts, characteristicDebts);
-    }
-
-    // Aggregate rules debt from children (and populate children characteristics debt)
-    for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) {
-      Long debt = measure.getValue().longValue();
-      RuleMeasure ruleMeasure = (RuleMeasure) measure;
-      total += computeDebt(debt, ruleMeasure.getRule().ruleKey(), ruleDebts, characteristicDebts);
-    }
-
-    context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue());
-    saveOnRule(context, ruleDebts);
-    for (Characteristic characteristic : model.characteristics()) {
-      Long debt = characteristicDebts.get(characteristic);
-      saveCharacteristicMeasure(context, characteristic, debt != null ? debt.doubleValue() : 0d, false);
-    }
-  }
-
-  private Long computeDebt(@Nullable Long debt, RuleKey ruleKey, SumMap<RuleKey> ruleDebts, SumMap<Characteristic> characteristicDebts) {
-    if (debt != null) {
-      Rule rule = rules.find(ruleKey);
-      if (rule != null) {
-        String characteristicKey = rule.debtSubCharacteristic();
-        if (characteristicKey != null) {
-          Characteristic characteristic = model.characteristicByKey(characteristicKey);
-          if (characteristic != null) {
-            ruleDebts.add(ruleKey, debt);
-            characteristicDebts.add(characteristic, debt);
-            propagateTechnicalDebtInParents(characteristic.parent(), debt, characteristicDebts);
-            return debt;
-          }
-        }
-      }
-    }
-    return 0L;
-  }
-
-  private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, long value, SumMap<Characteristic> characteristicDebts) {
-    if (characteristic != null) {
-      characteristicDebts.add(characteristic, value);
-      propagateTechnicalDebtInParents(characteristic.parent(), value, characteristicDebts);
-    }
-  }
-
-  private void saveOnRule(DecoratorContext context, SumMap<RuleKey> ruleDebts) {
-    for (Map.Entry<RuleKey, Long> entry : ruleDebts.entrySet()) {
-      org.sonar.api.rules.Rule oldRule = ruleFinder.findByKey(entry.getKey());
-      if (oldRule != null) {
-        saveRuleMeasure(context, oldRule, entry.getValue().doubleValue(), ResourceUtils.isEntity(context.getResource()));
-      }
-    }
-  }
-
-  @VisibleForTesting
-  void saveCharacteristicMeasure(DecoratorContext context, Characteristic characteristic, Double value, boolean inMemory) {
-    // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
-    // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
-    if (value > 0.0 || (ResourceUtils.isProject(context.getResource()) && characteristic.isRoot())) {
-      Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT);
-      measure.setCharacteristic(characteristic);
-      saveMeasure(context, measure, value, inMemory);
-    }
-  }
-
-  @VisibleForTesting
-  void saveRuleMeasure(DecoratorContext context, org.sonar.api.rules.Rule rule, Double value, boolean inMemory) {
-    // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
-    // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
-    if (value > 0.0) {
-      RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, rule, null, null);
-      saveMeasure(context, measure, value, inMemory);
-    }
-  }
-
-  private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) {
-    measure.setValue(value);
-    if (inMemory) {
-      measure.setPersistenceMode(PersistenceMode.MEMORY);
-    }
-    context.saveMeasure(measure);
-  }
-
-  private boolean shouldSaveMeasure(DecoratorContext context) {
-    return context.getMeasure(CoreMetrics.TECHNICAL_DEBT) == null;
-  }
-
-  public static List<PropertyDefinition> definitions() {
-    return ImmutableList.of(
-      PropertyDefinition.builder(CoreProperties.HOURS_IN_DAY)
-        .name("Number of working hours in a day")
-        .type(PropertyType.INTEGER)
-        .defaultValue("8")
-        .category(CoreProperties.CATEGORY_TECHNICAL_DEBT)
-        .deprecatedKey("sqale.hoursInDay")
-        .build()
-    );
-  }
-
-  private static class SumMap<E> {
-    private Map<E, Long> sumByKeys;
-
-    public SumMap() {
-      sumByKeys = newHashMap();
-    }
-
-    public void add(@Nullable E key, Long value) {
-      if (key != null) {
-        Long currentValue = sumByKeys.get(key);
-        sumByKeys.put(key, currentValue != null ? currentValue + value : value);
-      }
-    }
-
-    @CheckForNull
-    public Long get(E key) {
-      return sumByKeys.get(key);
-    }
-
-    public Set<Map.Entry<E, Long>> entrySet() {
-      return sumByKeys.entrySet();
-    }
-  }
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewDebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewDebtDecorator.java
deleted file mode 100644 (file)
index f5f7062..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.plugins.core.technicaldebt;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.batch.*;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.MeasureUtils;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.batch.components.Period;
-import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-
-import javax.annotation.Nullable;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * Decorator that computes the technical debt metric
- */
-@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
-public final class NewDebtDecorator implements Decorator {
-
-  private final ResourcePerspectives perspectives;
-  private final TimeMachineConfiguration timeMachineConfiguration;
-  private final IssueChangelogDebtCalculator issueChangelogDebtCalculator;
-
-  public NewDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration,
-                          IssueChangelogDebtCalculator issueChangelogDebtCalculator) {
-    this.perspectives = perspectives;
-    this.timeMachineConfiguration = timeMachineConfiguration;
-    this.issueChangelogDebtCalculator = issueChangelogDebtCalculator;
-  }
-
-  public boolean shouldExecuteOnProject(Project project) {
-    return true;
-  }
-
-  @DependedUpon
-  public List<Metric> generatesMetrics() {
-    return ImmutableList.of(
-      CoreMetrics.NEW_TECHNICAL_DEBT
-    );
-  }
-
-  public void decorate(Resource resource, DecoratorContext context) {
-    Issuable issuable = perspectives.as(Issuable.class, resource);
-    if (issuable != null && shouldSaveNewMetrics(context)) {
-      List<Issue> issues = newArrayList(issuable.issues());
-      saveMeasures(context, issues);
-    }
-  }
-
-  private void saveMeasures(DecoratorContext context, Collection<Issue> issues) {
-    Measure measure = new Measure(CoreMetrics.NEW_TECHNICAL_DEBT);
-    for (Period period : timeMachineConfiguration.periods()) {
-      Date periodDate = period.getDate();
-      double value = calculateNewTechnicalDebtValue(issues, periodDate);
-      Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
-      double sum = MeasureUtils.sumOnVariation(true, period.getIndex(), children) + value;
-      measure.setVariation(period.getIndex(), sum);
-    }
-    context.saveMeasure(measure);
-  }
-
-  private long calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) {
-    long result = 0;
-    for (Issue issue : issues) {
-      Long debt = issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, periodDate);
-      if (debt != null) {
-        result += debt;
-      }
-    }
-    return result;
-  }
-
-  private boolean shouldSaveNewMetrics(DecoratorContext context) {
-    return context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT) == null;
-  }
-
-}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/package-info.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/package-info.java
deleted file mode 100644 (file)
index defa4e0..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.plugins.core.technicaldebt;
-
-import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/ExclusionPropertiesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/ExclusionPropertiesTest.java
deleted file mode 100644 (file)
index 42aed67..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.core;
-
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.PropertyDefinition;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class ExclusionPropertiesTest {
-  @Test
-  public void definitions() throws Exception {
-    assertThat(ExclusionProperties.definitions().size()).isGreaterThan(0);
-    for (PropertyDefinition definition : ExclusionProperties.definitions()) {
-      assertThat(definition.category()).isEqualTo(CoreProperties.CATEGORY_EXCLUSIONS);
-    }
-  }
-}
index 8254fb280943c03fa8be159d4b9d86eb513c2af3..072dea5844edf45a94b13ea836b2d59c161b6dc5 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Resource;
+import org.sonar.core.config.ExclusionProperties;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -40,7 +41,7 @@ public class CoverageMeasurementFilterTest {
 
   @Before
   public void createFilter() {
-    settings = new Settings(new PropertyDefinitions(CoverageMeasurementFilter.getPropertyDefinitions()));
+    settings = new Settings(new PropertyDefinitions(ExclusionProperties.all()));
     filter = new CoverageMeasurementFilter(settings, new CoverageDecorator(), new LineCoverageDecorator(), new BranchCoverageDecorator());
   }
 
@@ -57,7 +58,7 @@ public class CoverageMeasurementFilterTest {
     Measure coverageMeasure = mock(Measure.class);
     when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER);
 
-    settings.setProperty(CoverageMeasurementFilter.PROPERTY_COVERAGE_EXCLUSIONS, "src/org/polop/*");
+    settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*");
     filter.initPatterns();
     assertThat(filter.accept(resource, coverageMeasure)).isFalse();
   }
@@ -68,7 +69,7 @@ public class CoverageMeasurementFilterTest {
     Measure coverageMeasure = mock(Measure.class);
     when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.COVERAGE);
 
-    settings.setProperty(CoverageMeasurementFilter.PROPERTY_COVERAGE_EXCLUSIONS, "src/org/other/*");
+    settings.setProperty("sonar.coverage.exclusions", "src/org/other/*");
     filter.initPatterns();
     assertThat(filter.accept(resource, coverageMeasure)).isTrue();
   }
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/DebtDecoratorTest.java
deleted file mode 100644 (file)
index 2aac580..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.plugins.core.technicaldebt;
-
-import com.google.common.collect.Lists;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
-import org.hamcrest.Description;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.batch.rule.internal.RulesBuilder;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.measures.*;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.test.IsMeasure;
-import org.sonar.api.utils.Duration;
-
-import java.util.Collections;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DebtDecoratorTest {
-
-  static final int HOURS_IN_DAY = 8;
-
-  static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
-
-  @Mock
-  DecoratorContext context;
-
-  @Mock
-  Resource resource;
-
-  @Mock
-  TechnicalDebtModel debtModel;
-
-  @Mock
-  Issuable issuable;
-
-  @Mock
-  ResourcePerspectives perspectives;
-
-  @Mock
-  RuleFinder ruleFinder;
-
-  RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
-  RuleKey ruleKey2 = RuleKey.of("repo2", "rule2");
-  Rules rules;
-
-  DefaultCharacteristic efficiency = new DefaultCharacteristic().setKey("EFFICIENCY");
-  DefaultCharacteristic memoryEfficiency = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(efficiency);
-
-  DefaultCharacteristic reusability = new DefaultCharacteristic().setKey("REUSABILITY");
-  DefaultCharacteristic modularity = new DefaultCharacteristic().setKey("MODULARITY").setParent(reusability);
-
-  DebtDecorator decorator;
-
-  @Before
-  public void before() throws Exception {
-    when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
-    RulesBuilder rulesBuilder = new RulesBuilder();
-    rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY");
-    rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY");
-    rules = rulesBuilder.build();
-
-    when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()));
-    when(ruleFinder.findByKey(ruleKey2)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()));
-
-    when(debtModel.characteristics()).thenReturn(newArrayList(efficiency, memoryEfficiency, reusability, modularity));
-    when(debtModel.characteristicByKey("EFFICIENCY")).thenReturn(efficiency);
-    when(debtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(memoryEfficiency);
-    when(debtModel.characteristicByKey("REUSABILITY")).thenReturn(reusability);
-    when(debtModel.characteristicByKey("MODULARITY")).thenReturn(modularity);
-
-    decorator = new DebtDecorator(perspectives, debtModel, rules, ruleFinder);
-  }
-
-  @Test
-  public void generates_metrics() throws Exception {
-    assertThat(decorator.generatesMetrics()).hasSize(1);
-  }
-
-  @Test
-  public void execute_on_project() throws Exception {
-    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
-  }
-
-  @Test
-  public void not_save_if_measure_already_computed() {
-    when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure());
-
-    decorator.decorate(resource, context);
-
-    verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT)));
-  }
-
-  @Test
-  public void add_technical_debt_from_one_issue_and_no_parent() throws Exception {
-    Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
-  }
-
-  @Test
-  public void add_technical_debt_from_one_issue_without_debt() throws Exception {
-    Issue issue = createIssue("rule1", "repo1").setDebt(null);
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0);
-  }
-
-  @Test
-  public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception {
-    Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, ONE_DAY_IN_MINUTES.doubleValue())));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, ONE_DAY_IN_MINUTES.doubleValue())));
-  }
-
-  @Test
-  public void add_technical_debt_from_issues() throws Exception {
-    Long technicalDebt1 = ONE_DAY_IN_MINUTES;
-    Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES;
-
-    Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
-    Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
-    Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
-    Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
-    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
-
-    decorator.decorate(resource, context);
-
-    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES);
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void add_technical_debt_from_current_and_children_measures() throws Exception {
-    Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
-    Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
-    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
-
-    when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
-      new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
-        org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null)
-        .setValue(5d * ONE_DAY_IN_MINUTES)
-    ));
-    decorator.decorate(resource, context);
-
-    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES);
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 7d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 7d * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void add_technical_debt_only_from_children_measures() throws Exception {
-    when(issuable.issues()).thenReturn(Collections.<Issue>emptyList());
-
-    when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
-      new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
-        org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule())
-        , null, null).setValue(5d * ONE_DAY_IN_MINUTES),
-
-      new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
-        org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())
-        , null, null).setValue(10d * ONE_DAY_IN_MINUTES)
-    ));
-    decorator.decorate(resource, context);
-
-    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES);
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 5d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 10d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 5d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 5d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, reusability, 10d * ONE_DAY_IN_MINUTES)));
-    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, modularity, 10d * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void always_save_technical_debt_for_positive_values() throws Exception {
-    // for a project
-    DecoratorContext context = mock(DecoratorContext.class);
-    when(context.getResource()).thenReturn(new Project("foo"));
-    decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
-    verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
-
-    // or for a file
-    context = mock(DecoratorContext.class);
-    when(context.getResource()).thenReturn(new File("foo"));
-    decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
-    verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
-  }
-
-  @Test
-  public void always_save_technical_debt_for_project_if_top_characteristic() throws Exception {
-    DecoratorContext context = mock(DecoratorContext.class);
-    when(context.getResource()).thenReturn(new Project("foo"));
-
-    // this is a top characteristic
-    DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("root");
-
-    decorator.saveCharacteristicMeasure(context, rootCharacteristic, 0.0, true);
-    verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT).setCharacteristic(rootCharacteristic));
-  }
-
-  /**
-   * SQALE-147
-   */
-  @Test
-  public void never_save_technical_debt_for_project_if_not_top_characteristic() throws Exception {
-    DecoratorContext context = mock(DecoratorContext.class);
-    when(context.getResource()).thenReturn(new Project("foo"));
-
-    DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY");
-    DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic);
-
-    decorator.saveCharacteristicMeasure(context, characteristic, 0.0, true);
-    verify(context, never()).saveMeasure(any(Measure.class));
-  }
-
-  @Test
-  public void not_save_technical_debt_for_file_if_zero() throws Exception {
-    DecoratorContext context = mock(DecoratorContext.class);
-    when(context.getResource()).thenReturn(new File("foo"));
-
-    decorator.saveCharacteristicMeasure(context, (Characteristic) null, 0.0, true);
-    verify(context, never()).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
-  }
-
-  @Test
-  public void check_definitions() {
-    assertThat(DebtDecorator.definitions()).hasSize(1);
-  }
-
-  private DefaultIssue createIssue(String ruleKey, String repositoryKey) {
-    return new DefaultIssue().setRuleKey(RuleKey.of(repositoryKey, ruleKey));
-  }
-
-  class IsCharacteristicMeasure extends ArgumentMatcher<Measure> {
-    Metric metric = null;
-    Characteristic characteristic = null;
-    Double value = null;
-
-    public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) {
-      this.metric = metric;
-      this.characteristic = characteristic;
-      this.value = value;
-    }
-
-    @Override
-    public boolean matches(Object o) {
-      if (!(o instanceof Measure)) {
-        return false;
-      }
-      Measure m = (Measure) o;
-      return ObjectUtils.equals(metric, m.getMetric()) &&
-        ObjectUtils.equals(characteristic, m.getCharacteristic()) &&
-        ObjectUtils.equals(value, m.getValue());
-    }
-
-    @Override
-    public void describeTo(Description description) {
-      description.appendText(new StringBuilder()
-        .append("value=").append(value).append(",")
-        .append("characteristic=").append(characteristic.key()).append(",")
-        .append("metric=").append(metric.getKey()).toString())
-      ;
-    }
-  }
-
-  class IsRuleMeasure extends ArgumentMatcher<RuleMeasure> {
-    Metric metric = null;
-    RuleKey ruleKey = null;
-    Double value = null;
-
-    public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) {
-      this.metric = metric;
-      this.ruleKey = ruleKey;
-      this.value = value;
-    }
-
-    @Override
-    public boolean matches(Object o) {
-      if (!(o instanceof RuleMeasure)) {
-        return false;
-      }
-      RuleMeasure m = (RuleMeasure) o;
-      return ObjectUtils.equals(metric, m.getMetric()) &&
-        ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) &&
-        ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) &&
-        ObjectUtils.equals(value, m.getValue());
-    }
-
-    @Override
-    public void describeTo(Description description) {
-      description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE));
-    }
-  }
-}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewDebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewDebtDecoratorTest.java
deleted file mode 100644 (file)
index a9391f9..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.plugins.core.technicaldebt;
-
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.time.DateUtils;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.test.IsMeasure;
-import org.sonar.api.utils.Duration;
-import org.sonar.batch.components.Period;
-import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-
-import java.util.Date;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class NewDebtDecoratorTest {
-
-  NewDebtDecorator decorator;
-
-  @Mock
-  TimeMachineConfiguration timeMachineConfiguration;
-
-  @Mock
-  Resource resource;
-
-  @Mock
-  Issuable issuable;
-
-  @Mock
-  DecoratorContext context;
-
-  Date rightNow;
-  Date elevenDaysAgo;
-  Date tenDaysAgo;
-  Date nineDaysAgo;
-  Date fiveDaysAgo;
-  Date fourDaysAgo;
-
-  static final int HOURS_IN_DAY = 8;
-
-  static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
-  static final Long TWO_DAYS_IN_MINUTES = 2L * HOURS_IN_DAY * 60;
-  static final Long FIVE_DAYS_IN_MINUTES = 5L * HOURS_IN_DAY * 60;
-
-  @Before
-  public void setup() {
-    Settings settings = new Settings();
-    settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
-
-    ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
-    when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
-
-    rightNow = new Date();
-    elevenDaysAgo = DateUtils.addDays(rightNow, -11);
-    tenDaysAgo = DateUtils.addDays(rightNow, -10);
-    nineDaysAgo = DateUtils.addDays(rightNow, -9);
-    fiveDaysAgo = DateUtils.addDays(rightNow, -5);
-    fourDaysAgo = DateUtils.addDays(rightNow, -4);
-
-    when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo)));
-
-    decorator = new NewDebtDecorator(perspectives, timeMachineConfiguration, new IssueChangelogDebtCalculator());
-  }
-
-  @Test
-  public void generates_metrics() throws Exception {
-    assertThat(decorator.generatesMetrics()).hasSize(1);
-  }
-
-  @Test
-  public void execute_on_project() throws Exception {
-    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
-  }
-
-  @Test
-  public void save_on_one_issue_with_one_new_changelog() {
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        // changelog created at is null because it has just been created on the current analysis
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0 * ONE_DAY_IN_MINUTES, 1.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_one_issue_with_changelog() {
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_one_issue_with_changelog_only_in_the_past() {
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
-      newArrayList(
-        // Change before all periods
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
-  }
-
-  @Test
-  public void save_on_one_issue_with_changelog_having_null_value() {
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
-    when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
-
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() {
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs()
-          .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo)
-          .setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_issues_with_changelog() {
-    Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-      )
-    );
-    Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 7.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_one_issue_without_changelog() {
-    when(issuable.issues()).thenReturn(newArrayList(
-      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
-    );
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_one_issue_without_technical_debt_and_without_changelog() {
-    when(issuable.issues()).thenReturn(newArrayList(
-      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(null))
-    );
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
-  }
-
-  @Test
-  public void save_on_one_issue_without_changelog_and_periods_have_no_dates() {
-    when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
-
-    when(issuable.issues()).thenReturn(newArrayList(
-      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
-    );
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is null, period2 is null
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_issues_without_changelog() {
-    when(issuable.issues()).thenReturn(newArrayList(
-      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
-      new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
-    ));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void save_on_issues_with_changelog_and_issues_without_changelog() {
-    // issue1 and issue2 have changelog
-    Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-      )
-    );
-    Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
-      newArrayList(
-        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
-        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-      )
-    );
-
-    // issue3 and issue4 have no changelog
-    Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES));
-    Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES));
-    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 14.0 * ONE_DAY_IN_MINUTES)));
-  }
-
-  @Test
-  public void not_save_if_measure_already_computed() {
-    when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure());
-    when(issuable.issues()).thenReturn(newArrayList(
-      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
-      new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
-    ));
-
-    decorator.decorate(resource, context);
-
-    verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
-  }
-
-  /**
-   * SONAR-5059
-   */
-  @Test
-  public void not_return_negative_debt() {
-    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
-      newArrayList(
-        // changelog created at is null because it has just been created on the current analysis
-        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null)
-      )
-    );
-    when(issuable.issues()).thenReturn(newArrayList(issue));
-
-    decorator.decorate(resource, context);
-
-    // remember : period1 is 5daysAgo, period2 is 10daysAgo
-    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
-  }
-
-
-  class IsVariationMeasure extends ArgumentMatcher<Measure> {
-    Metric metric = null;
-    Double var1 = null;
-    Double var2 = null;
-
-    public IsVariationMeasure(Metric metric, Double var1, Double var2) {
-      this.metric = metric;
-      this.var1 = var1;
-      this.var2 = var2;
-    }
-
-    public boolean matches(Object o) {
-      if (!(o instanceof Measure)) {
-        return false;
-      }
-      Measure m = (Measure) o;
-      return ObjectUtils.equals(metric, m.getMetric()) &&
-        ObjectUtils.equals(var1, m.getVariation1()) &&
-        ObjectUtils.equals(var2, m.getVariation2());
-    }
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/pom.xml b/plugins/sonar-maven-batch-plugin/pom.xml
deleted file mode 100644 (file)
index 20af0ad..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.codehaus.sonar</groupId>
-    <artifactId>sonar</artifactId>
-    <version>4.3-SNAPSHOT</version>
-    <relativePath>../..</relativePath>
-  </parent>
-
-  <groupId>org.codehaus.sonar.plugins</groupId>
-  <artifactId>sonar-maven-batch-plugin</artifactId>
-  <packaging>sonar-plugin</packaging>
-  <name>SonarQube :: Maven Batch Plugin</name>
-
-  <properties>
-    <maven.version>3.0</maven.version>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>com.google.code.findbugs</groupId>
-      <artifactId>jsr305</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.codehaus.sonar</groupId>
-      <artifactId>sonar-batch</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.codehaus.sonar</groupId>
-      <artifactId>sonar-plugin-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-plugin-api</artifactId>
-      <version>${maven.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
-      <version>${maven.version}</version>
-      <scope>provided</scope>
-    </dependency>
-
-    <!-- unit tests -->
-    <dependency>
-      <groupId>org.codehaus.sonar</groupId>
-      <artifactId>sonar-testing-harness</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-all</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-
-      <plugin>
-        <groupId>org.codehaus.sonar</groupId>
-        <artifactId>sonar-packaging-maven-plugin</artifactId>
-        <configuration>
-          <pluginName>Maven Batch Plugin</pluginName>
-          <pluginClass>org.sonar.plugins.maven.MavenBatchPlugin</pluginClass>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/DefaultMavenPluginExecutor.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/DefaultMavenPluginExecutor.java
deleted file mode 100644 (file)
index fc5eef9..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.execution.ReactorManager;
-import org.apache.maven.lifecycle.LifecycleExecutor;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.maven.MavenPlugin;
-import org.sonar.api.batch.maven.MavenPluginHandler;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.SonarException;
-import org.sonar.api.utils.TimeProfiler;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.batch.scan.maven.MavenPluginExecutor;
-
-import javax.annotation.Nullable;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
-@SupportedEnvironment("maven")
-public class DefaultMavenPluginExecutor implements MavenPluginExecutor {
-
-  private final MavenProjectConverter pomConverter;
-  private LifecycleExecutor lifecycleExecutor;
-  private MavenSession mavenSession;
-
-  public DefaultMavenPluginExecutor(LifecycleExecutor le, MavenSession mavenSession, MavenProjectConverter pomConverter) {
-    this.lifecycleExecutor = le;
-    this.mavenSession = mavenSession;
-    this.pomConverter = pomConverter;
-  }
-
-  @Override
-  public final MavenPluginHandler execute(Project project, DefaultModuleFileSystem fs, MavenPluginHandler handler) {
-    for (String goal : handler.getGoals()) {
-      if (goal == null) {
-        throw new IllegalStateException("Maven goal can't be null");
-      }
-      MavenPlugin plugin = MavenPlugin.getPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId());
-      execute(project,
-        fs,
-        getGoal(handler.getGroupId(), handler.getArtifactId(), plugin != null && plugin.getPlugin() != null ? plugin.getPlugin().getVersion() : null, goal));
-    }
-    return handler;
-  }
-
-  @Override
-  public final void execute(Project project, DefaultModuleFileSystem fs, String goal) {
-    if (project.getPom() != null) {
-      TimeProfiler profiler = new TimeProfiler().start("Execute " + goal);
-      ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
-      try {
-        concreteExecute(project.getPom(), goal);
-      } catch (Exception e) {
-        throw new SonarException("Unable to execute maven plugin", e);
-      } finally {
-        // Reset original ClassLoader that may have been changed during Maven Execution (see SONAR-1800)
-        Thread.currentThread().setContextClassLoader(currentClassLoader);
-        profiler.stop();
-      }
-      if (!fs.isInitialized()) {
-        pomConverter.synchronizeFileSystem(project.getPom(), fs);
-      }
-    }
-  }
-
-  static String getGoal(String groupId, String artifactId, @Nullable String version, String goal) {
-    String defaultVersion = version == null ? "" : version;
-    return new StringBuilder()
-      .append(groupId).append(":")
-      .append(artifactId).append(":")
-      .append(defaultVersion)
-      .append(":")
-      .append(goal)
-      .toString();
-  }
-
-  public void concreteExecute(MavenProject pom, String goal) {
-    Method executeMethod = null;
-    for (Method m : lifecycleExecutor.getClass().getMethods()) {
-      if ("execute".equals(m.getName())) {
-        executeMethod = m;
-        break;
-      }
-    }
-    if (executeMethod == null) {
-      throw new SonarException("Unable to find execute method on Maven LifecycleExecutor. Please check your Maven version.");
-    }
-    if (executeMethod.getParameterTypes().length == 1) {
-      concreteExecuteMaven3(pom, goal);
-    } else if (executeMethod.getParameterTypes().length == 3) {
-      concreteExecuteMaven2(executeMethod, pom, goal);
-    } else {
-      throw new SonarException("Unexpected parameter count on Maven LifecycleExecutor#execute method. Please check your Maven version.");
-    }
-  }
-
-  public void concreteExecuteMaven3(MavenProject pom, String goal) {
-    MavenSession projectSession = mavenSession.clone();
-    projectSession.setCurrentProject(pom);
-    projectSession.setProjects(Arrays.asList(pom));
-    projectSession.getRequest().setRecursive(false);
-    projectSession.getRequest().setPom(pom.getFile());
-    projectSession.getRequest().setGoals(Arrays.asList(goal));
-    projectSession.getRequest().setInteractiveMode(false);
-    lifecycleExecutor.execute(projectSession);
-    if (projectSession.getResult().hasExceptions()) {
-      throw new SonarException("Exception during execution of " + goal);
-    }
-  }
-
-  public void concreteExecuteMaven2(Method executeMethod, MavenProject pom, String goal) {
-    try {
-      ReactorManager reactor = new ReactorManager(Arrays.asList(pom));
-      MavenSession clonedSession = new MavenSession(mavenSession.getContainer(),
-        mavenSession.getSettings(),
-        mavenSession.getLocalRepository(),
-        mavenSession.getEventDispatcher(),
-        reactor,
-        Arrays.asList(goal),
-        mavenSession.getExecutionRootDirectory(),
-        mavenSession.getExecutionProperties(),
-        mavenSession.getStartTime());
-      executeMethod.invoke(lifecycleExecutor, clonedSession, reactor, clonedSession.getEventDispatcher());
-    } catch (Exception e) {
-      throw new SonarException("Unable to execute Maven 2 plugin", e);
-    }
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenBatchPlugin.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenBatchPlugin.java
deleted file mode 100644 (file)
index 64375e5..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.SonarPlugin;
-
-import java.util.List;
-
-public final class MavenBatchPlugin extends SonarPlugin {
-
-  public List getExtensions() {
-    return ImmutableList.of(MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class);
-  }
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectBootstrapper.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectBootstrapper.java
deleted file mode 100644 (file)
index 1bb3ea3..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-import java.util.List;
-
-/**
- * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
- */
-@Deprecated
-@SupportedEnvironment("maven")
-public class MavenProjectBootstrapper implements ProjectBootstrapper {
-
-  private MavenSession session;
-  private MavenProjectConverter mavenProjectConverter;
-
-  public MavenProjectBootstrapper(MavenSession session, MavenProjectConverter mavenProjectConverter) {
-    this.session = session;
-    this.mavenProjectConverter = mavenProjectConverter;
-  }
-
-  @Override
-  public ProjectReactor bootstrap() {
-    // Don't use session.getTopLevelProject or session.getProjects to keep compatibility with Maven 2
-    List<MavenProject> sortedProjects = session.getSortedProjects();
-    MavenProject topLevelProject = null;
-    for (MavenProject project : sortedProjects) {
-      if (project.isExecutionRoot()) {
-        topLevelProject = project;
-        break;
-      }
-    }
-    if (topLevelProject == null) {
-      throw new IllegalStateException("Maven session does not declare a top level project");
-    }
-    return new ProjectReactor(mavenProjectConverter.configure(sortedProjects, topLevelProject));
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectBuilder.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectBuilder.java
deleted file mode 100644 (file)
index 3d9ebb1..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.bootstrap.ProjectBuilder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-import java.util.List;
-
-/**
- * Class that inject MavenProject in each module container
- */
-@SupportedEnvironment("maven")
-public class MavenProjectBuilder extends ProjectBuilder {
-
-  private final MavenSession mavenSession;
-
-  public MavenProjectBuilder(MavenSession mavenSession) {
-    this.mavenSession = mavenSession;
-  }
-
-  @Override
-  public void build(Context context) {
-    ProjectReactor reactor = context.projectReactor();
-    for (ProjectDefinition moduleDef : reactor.getProjects()) {
-      setMavenProjectIfApplicable(moduleDef);
-    }
-  }
-
-  private void setMavenProjectIfApplicable(ProjectDefinition definition) {
-    if (mavenSession != null) {
-      String moduleKey = definition.getKey();
-      for (MavenProject mavenModule : (List<MavenProject>) mavenSession.getSortedProjects()) {
-        String mavenModuleKey = mavenModule.getGroupId() + ":" + mavenModule.getArtifactId();
-        if (mavenModuleKey.equals(moduleKey) && !definition.getContainerExtensions().contains(mavenModule)) {
-          definition.addContainerExtension(mavenModule);
-        }
-      }
-    }
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/MavenProjectConverter.java
deleted file mode 100644 (file)
index 3162b09..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import org.apache.commons.lang.StringUtils;
-import org.apache.maven.model.CiManagement;
-import org.apache.maven.model.IssueManagement;
-import org.apache.maven.model.Scm;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.maven.MavenUtils;
-import org.sonar.api.task.TaskExtension;
-import org.sonar.api.utils.MessageException;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.java.api.JavaUtils;
-
-import javax.annotation.Nullable;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
- */
-@Deprecated
-@SupportedEnvironment("maven")
-public class MavenProjectConverter implements TaskExtension {
-
-  private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
-    " Probably you use Maven Advanced Reactor Options, which is not supported by SonarQube and should not be used.";
-
-  public ProjectDefinition configure(List<MavenProject> poms, MavenProject root) {
-    // projects by canonical path to pom.xml
-    Map<String, MavenProject> paths = Maps.newHashMap();
-    Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();
-
-    try {
-      configureModules(poms, paths, defs);
-
-      rebuildModuleHierarchy(paths, defs);
-    } catch (IOException e) {
-      throw new IllegalStateException("Cannot configure project", e);
-    }
-
-    ProjectDefinition rootProject = defs.get(root);
-    if (rootProject == null) {
-      throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
-    }
-    return rootProject;
-  }
-
-  private void rebuildModuleHierarchy(Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
-    for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
-      MavenProject pom = entry.getValue();
-      for (Object m : pom.getModules()) {
-        String moduleId = (String) m;
-        File modulePath = new File(pom.getBasedir(), moduleId);
-        MavenProject module = findMavenProject(modulePath, paths);
-
-        ProjectDefinition parentProject = defs.get(pom);
-        if (parentProject == null) {
-          throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
-        }
-        ProjectDefinition subProject = defs.get(module);
-        if (subProject == null) {
-          throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
-        }
-        parentProject.addSubProject(subProject);
-      }
-    }
-  }
-
-  private void configureModules(List<MavenProject> poms, Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
-    for (MavenProject pom : poms) {
-      paths.put(pom.getFile().getCanonicalPath(), pom);
-      ProjectDefinition def = ProjectDefinition.create();
-      merge(pom, def);
-      defs.put(pom, def);
-    }
-  }
-
-  private static MavenProject findMavenProject(final File modulePath, Map<String, MavenProject> paths) throws IOException {
-    if (modulePath.exists() && modulePath.isDirectory()) {
-      for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
-        String pomFileParentDir = new File(entry.getKey()).getParent();
-        if (pomFileParentDir.equals(modulePath.getCanonicalPath())) {
-          return entry.getValue();
-        }
-      }
-      return null;
-    }
-    return paths.get(modulePath.getCanonicalPath());
-  }
-
-  @VisibleForTesting
-  void merge(MavenProject pom, ProjectDefinition definition) {
-    String key = getSonarKey(pom);
-    // IMPORTANT NOTE : reference on properties from POM model must not be saved,
-    // instead they should be copied explicitly - see SONAR-2896
-    definition
-      .setProperties(pom.getModel().getProperties())
-      .setKey(key)
-      .setVersion(pom.getVersion())
-      .setName(pom.getName())
-      .setDescription(pom.getDescription())
-      .addContainerExtension(pom);
-    guessJavaVersion(pom, definition);
-    guessEncoding(pom, definition);
-    convertMavenLinksToProperties(definition, pom);
-    synchronizeFileSystem(pom, definition);
-  }
-
-  private static String getSonarKey(MavenProject pom) {
-    return new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
-  }
-
-  private static void guessEncoding(MavenProject pom, ProjectDefinition definition) {
-    // See http://jira.codehaus.org/browse/SONAR-2151
-    String encoding = MavenUtils.getSourceEncoding(pom);
-    if (encoding != null) {
-      definition.setProperty(CoreProperties.ENCODING_PROPERTY, encoding);
-    }
-  }
-
-  private static void guessJavaVersion(MavenProject pom, ProjectDefinition definition) {
-    // See http://jira.codehaus.org/browse/SONAR-2148
-    // Get Java source and target versions from maven-compiler-plugin.
-    String version = MavenUtils.getJavaSourceVersion(pom);
-    if (version != null) {
-      definition.setProperty(JavaUtils.JAVA_SOURCE_PROPERTY, version);
-    }
-    version = MavenUtils.getJavaVersion(pom);
-    if (version != null) {
-      definition.setProperty(JavaUtils.JAVA_TARGET_PROPERTY, version);
-    }
-  }
-
-  /**
-   * For SONAR-3676
-   */
-  private static void convertMavenLinksToProperties(ProjectDefinition definition, MavenProject pom) {
-    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_HOME_PAGE, pom.getUrl());
-
-    Scm scm = pom.getScm();
-    if (scm == null) {
-      scm = new Scm();
-    }
-    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES, scm.getUrl());
-    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES_DEV, scm.getDeveloperConnection());
-
-    CiManagement ci = pom.getCiManagement();
-    if (ci == null) {
-      ci = new CiManagement();
-    }
-    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_CI, ci.getUrl());
-
-    IssueManagement issues = pom.getIssueManagement();
-    if (issues == null) {
-      issues = new IssueManagement();
-    }
-    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_ISSUE_TRACKER, issues.getUrl());
-  }
-
-  private static void setPropertyIfNotAlreadyExists(ProjectDefinition definition, String propertyKey, String propertyValue) {
-    if (StringUtils.isBlank(definition.getProperties().getProperty(propertyKey))) {
-      definition.setProperty(propertyKey, StringUtils.defaultString(propertyValue));
-    }
-  }
-
-  public void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
-    into.setBaseDir(pom.getBasedir());
-    File buildDir = getBuildDir(pom);
-    if (buildDir != null) {
-      into.setBuildDir(buildDir);
-      into.setWorkDir(getSonarWorkDir(pom));
-    }
-    into.setSourceDirs(toPaths(mainDirs(pom)));
-    into.setTestDirs(toPaths(testDirs(pom)));
-    File binaryDir = resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir());
-    if (binaryDir != null) {
-      into.addBinaryDir(binaryDir);
-    }
-  }
-
-  public static File getSonarWorkDir(MavenProject pom) {
-    return new File(getBuildDir(pom), "sonar");
-  }
-
-  private static File getBuildDir(MavenProject pom) {
-    return resolvePath(pom.getBuild().getDirectory(), pom.getBasedir());
-  }
-
-  public void synchronizeFileSystem(MavenProject pom, DefaultModuleFileSystem into) {
-    into.resetDirs(
-      pom.getBasedir(),
-      getBuildDir(pom),
-      mainDirs(pom),
-      testDirs(pom),
-      Arrays.asList(resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir())));
-  }
-
-  static File resolvePath(@Nullable String path, File basedir) {
-    if (path != null) {
-      File file = new File(StringUtils.trim(path));
-      if (!file.isAbsolute()) {
-        try {
-          file = new File(basedir, path).getCanonicalFile();
-        } catch (IOException e) {
-          throw new IllegalStateException("Unable to resolve path '" + path + "'", e);
-        }
-      }
-      return file;
-    }
-    return null;
-  }
-
-  static List<File> resolvePaths(List<String> paths, File basedir) {
-    List<File> result = Lists.newArrayList();
-    for (String path : paths) {
-      File dir = resolvePath(path, basedir);
-      if (dir != null) {
-        result.add(dir);
-      }
-    }
-    return result;
-  }
-
-  private List<File> mainDirs(MavenProject pom) {
-    return sourceDirs(pom, ProjectDefinition.SOURCE_DIRS_PROPERTY, pom.getCompileSourceRoots());
-  }
-
-  private List<File> testDirs(MavenProject pom) {
-    return sourceDirs(pom, ProjectDefinition.TEST_DIRS_PROPERTY, pom.getTestCompileSourceRoots());
-  }
-
-  private List<File> sourceDirs(MavenProject pom, String propertyKey, List mavenDirs) {
-    List<String> paths;
-    String prop = pom.getProperties().getProperty(propertyKey);
-    if (prop != null) {
-      paths = Arrays.asList(StringUtils.split(prop, ","));
-      // do not remove dirs that do not exist. They must be kept in order to
-      // notify users that value of sonar.sources has a typo.
-      return existingDirsOrFail(resolvePaths(paths, pom.getBasedir()), pom, propertyKey);
-    }
-
-    List<File> dirs = resolvePaths(mavenDirs, pom.getBasedir());
-
-    // Maven provides some directories that do not exist. They
-    // should be removed
-    return keepExistingDirs(dirs);
-  }
-
-  private List<File> existingDirsOrFail(List<File> dirs, MavenProject pom, String propertyKey) {
-    for (File dir : dirs) {
-      if (!dir.isDirectory() || !dir.exists()) {
-        throw MessageException.of(String.format(
-          "The directory '%s' does not exist for Maven module %s. Please check the property %s",
-          dir.getAbsolutePath(), pom.getId(), propertyKey));
-      }
-    }
-    return dirs;
-  }
-
-  private static List<File> keepExistingDirs(List<File> files) {
-    return Lists.newArrayList(Collections2.filter(files, new Predicate<File>() {
-      @Override
-      public boolean apply(File dir) {
-        return dir != null && dir.exists() && dir.isDirectory();
-      }
-    }));
-  }
-
-  private static String[] toPaths(Collection<File> dirs) {
-    Collection<String> paths = Collections2.transform(dirs, new Function<File, String>() {
-      @Override
-      public String apply(File dir) {
-        return dir.getAbsolutePath();
-      }
-    });
-    return paths.toArray(new String[paths.size()]);
-  }
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/package-info.java b/plugins/sonar-maven-batch-plugin/src/main/java/org/sonar/plugins/maven/package-info.java
deleted file mode 100644 (file)
index 4e7cfd8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-/**
- * This package is a part of bootstrap process, so we should take care about backward compatibility.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.plugins.maven;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
diff --git a/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/DefaultMavenPluginExecutorTest.java b/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/DefaultMavenPluginExecutorTest.java
deleted file mode 100644 (file)
index fcaa0bf..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.maven.project.MavenProject;
-import org.junit.Test;
-import org.sonar.api.batch.maven.MavenPlugin;
-import org.sonar.api.batch.maven.MavenPluginHandler;
-import org.sonar.api.resources.Project;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-
-import java.io.File;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyList;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-public class DefaultMavenPluginExecutorTest {
-
-  @Test
-  public void plugin_version_should_be_optional() {
-    assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", null, "goal"), is("group:artifact::goal"));
-  }
-
-  @Test
-  public void test_plugin_version() {
-    assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", "3.54", "goal"), is("group:artifact:3.54:goal"));
-  }
-
-  @Test
-  public void should_ignore_non_maven_projects() {
-    DefaultMavenPluginExecutor executor = new DefaultMavenPluginExecutor(null, null, mock(MavenProjectConverter.class)) {
-      @Override
-      public void concreteExecute(MavenProject pom, String goal) {
-        pom.addCompileSourceRoot("src/java");
-      }
-    };
-    Project foo = new Project("foo");
-    DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
-    executor.execute(foo, fs, new AddSourceMavenPluginHandler());
-
-    verify(fs, never()).resetDirs(any(File.class), any(File.class), anyList(), anyList(), anyList());
-  }
-
-  static class AddSourceMavenPluginHandler implements MavenPluginHandler {
-    public String getGroupId() {
-      return "fake";
-    }
-
-    public String getArtifactId() {
-      return "fake";
-    }
-
-    public String getVersion() {
-      return "2.2";
-    }
-
-    public boolean isFixedVersion() {
-      return false;
-    }
-
-    public String[] getGoals() {
-      return new String[]{"fake"};
-    }
-
-    public void configure(Project project, MavenPlugin plugin) {
-    }
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenBatchPluginTest.java b/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenBatchPluginTest.java
deleted file mode 100644 (file)
index 0504cb9..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.junit.Test;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class MavenBatchPluginTest {
-
-  @Test
-  public void testGetExtensions() {
-    MavenBatchPlugin plugin = new MavenBatchPlugin();
-    assertThat(plugin.getExtensions()).hasSize(4);
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenProjectBootstrapperTest.java b/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenProjectBootstrapperTest.java
deleted file mode 100644 (file)
index 9c7cb4e..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class MavenProjectBootstrapperTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  @Test
-  public void bootstrap() throws Exception {
-    ProjectDefinition def = mock(ProjectDefinition.class);
-    MavenSession session = mock(MavenSession.class);
-    MavenProject rootProject = new MavenProject();
-    rootProject.setExecutionRoot(true);
-    List<MavenProject> projects = Arrays.asList(rootProject);
-    when(session.getSortedProjects()).thenReturn(projects);
-
-    MavenProjectConverter pomConverter = mock(MavenProjectConverter.class);
-    when(pomConverter.configure(projects, rootProject)).thenReturn(def);
-    MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
-
-    ProjectReactor reactor = bootstrapper.bootstrap();
-
-    assertThat(reactor).isNotNull();
-    verify(pomConverter).configure(projects, rootProject);
-  }
-
-  @Test
-  public void should_fail_if_no_top_level_project() throws Exception {
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("Maven session does not declare a top level project");
-
-    MavenSession session = mock(MavenSession.class);
-    MavenProjectConverter pomConverter = new MavenProjectConverter();
-    MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
-
-    bootstrapper.bootstrap();
-  }
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenProjectConverterTest.java b/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/MavenProjectConverterTest.java
deleted file mode 100644 (file)
index 6cc799f..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.maven.model.Model;
-import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
-import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
-import org.hamcrest.core.Is;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.test.TestUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.URISyntaxException;
-import java.util.Arrays;
-import java.util.Properties;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-public class MavenProjectConverterTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  MavenProjectConverter converter = new MavenProjectConverter();
-
-  /**
-   * See SONAR-2681
-   */
-  @Test
-  public void shouldThrowExceptionWhenUnableToDetermineProjectStructure() {
-    MavenProject root = new MavenProject();
-    root.setFile(new File("/foo/pom.xml"));
-    root.getBuild().setDirectory("target");
-    root.getModules().add("module/pom.xml");
-
-    try {
-      converter.configure(Arrays.asList(root), root);
-      fail();
-    } catch (IllegalStateException e) {
-      assertThat(e.getMessage(), containsString("Advanced Reactor Options"));
-    }
-  }
-
-  @Test
-  public void shouldConvertModules() throws IOException {
-    File basedir = temp.newFolder();
-
-    MavenProject root = newMavenProject("com.foo", "parent", "1.0-SNAPSHOT");
-    root.setFile(new File(basedir, "pom.xml"));
-    root.getBuild().setDirectory("target");
-    root.getBuild().setOutputDirectory("target/classes");
-    root.getModules().add("module/pom.xml");
-    MavenProject module = newMavenProject("com.foo", "moduleA", "1.0-SNAPSHOT");
-    module.setFile(new File(basedir, "module/pom.xml"));
-    module.getBuild().setDirectory("target");
-    module.getBuild().setOutputDirectory("target/classes");
-    ProjectDefinition project = converter.configure(Arrays.asList(root, module), root);
-
-    assertThat(project.getSubProjects().size(), is(1));
-  }
-
-  private MavenProject newMavenProject(String groupId, String artifactId, String version) {
-    Model model = new Model();
-    model.setGroupId(groupId);
-    model.setArtifactId(artifactId);
-    model.setVersion(version);
-    return new MavenProject(model);
-  }
-
-  @Test
-  public void shouldConvertProperties() {
-    MavenProject pom = new MavenProject();
-    pom.setGroupId("foo");
-    pom.setArtifactId("bar");
-    pom.setVersion("1.0.1");
-    pom.setName("Test");
-    pom.setDescription("just test");
-    pom.setFile(new File("/foo/pom.xml"));
-    pom.getBuild().setDirectory("target");
-    ProjectDefinition project = ProjectDefinition.create();
-    converter.merge(pom, project);
-
-    Properties properties = project.getProperties();
-    assertThat(properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY), is("foo:bar"));
-    assertThat(properties.getProperty(CoreProperties.PROJECT_VERSION_PROPERTY), is("1.0.1"));
-    assertThat(properties.getProperty(CoreProperties.PROJECT_NAME_PROPERTY), is("Test"));
-    assertThat(properties.getProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY), is("just test"));
-  }
-
-  @Test
-  public void moduleNameShouldEqualArtifactId() throws Exception {
-    File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/");
-    MavenProject parent = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml", true);
-    MavenProject module1 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml", false);
-    MavenProject module2 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml", false);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
-
-    assertThat(rootDef.getSubProjects().size(), Is.is(2));
-    assertThat(rootDef.getKey(), Is.is("org.test:parent"));
-    assertNull(rootDef.getParent());
-    assertThat(rootDef.getBaseDir(), is(rootDir));
-
-    ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
-    assertThat(module1Def.getKey(), Is.is("org.test:module1"));
-    assertThat(module1Def.getParent(), Is.is(rootDef));
-    assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module1")));
-    assertThat(module1Def.getSubProjects().size(), Is.is(0));
-  }
-
-  @Test
-  public void moduleNameDifferentThanArtifactId() throws Exception {
-    File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/");
-    MavenProject parent = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml", true);
-    MavenProject module1 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml", false);
-    MavenProject module2 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml", false);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
-
-    assertThat(rootDef.getSubProjects().size(), is(2));
-    assertThat(rootDef.getKey(), is("org.test:parent"));
-    assertNull(rootDef.getParent());
-    assertThat(rootDef.getBaseDir(), is(rootDir));
-
-    ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
-    assertThat(module1Def.getKey(), Is.is("org.test:module1"));
-    assertThat(module1Def.getParent(), Is.is(rootDef));
-    assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "path1")));
-    assertThat(module1Def.getSubProjects().size(), Is.is(0));
-  }
-
-  @Test
-  public void should_find_module_with_maven_project_file_naming_different_from_pom_xml() throws Exception {
-    File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/");
-    MavenProject parent = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml", true);
-    MavenProject module = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml", false);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module), parent);
-
-    assertThat(rootDef.getSubProjects().size(), Is.is(1));
-    assertThat(rootDef.getKey(), Is.is("org.test:parent"));
-    assertNull(rootDef.getParent());
-    assertThat(rootDef.getBaseDir(), is(rootDir));
-
-    ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
-    assertThat(module1Def.getKey(), Is.is("org.test:module"));
-    assertThat(module1Def.getParent(), Is.is(rootDef));
-    assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module")));
-    assertThat(module1Def.getSubProjects().size(), Is.is(0));
-  }
-
-  @Test
-  public void testSingleProjectWithoutModules() throws Exception {
-    File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/");
-    MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml", true);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
-    assertThat(rootDef.getKey(), is("org.test:parent"));
-    assertThat(rootDef.getSubProjects().size(), is(0));
-    assertNull(rootDef.getParent());
-    assertThat(rootDef.getBaseDir(), is(rootDir));
-  }
-
-  @Test
-  public void shouldConvertLinksToProperties() throws Exception {
-    MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinks/pom.xml", true);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
-    Properties props = rootDef.getProperties();
-    assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com");
-    assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
-    assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
-    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com");
-    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
-  }
-
-  @Test
-  public void shouldNotConvertLinksToPropertiesIfPropertyAlreadyDefined() throws Exception {
-    MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml", true);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
-    Properties props = rootDef.getProperties();
-
-    // Those properties have been fed by the POM elements <ciManagement>, <issueManagement>, ...
-    assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
-    assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
-    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
-
-    // ... but those ones have been overridden by <properties> in the POM
-    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com-OVERRIDEN-BY-PROPS");
-    assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com-OVERRIDEN-BY-PROPS");
-  }
-
-  @Test
-  public void shouldLoadSourceEncoding() throws Exception {
-    MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/sourceEncoding/pom.xml", true);
-
-    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
-    assertThat(rootDef.getProperties().getProperty(CoreProperties.ENCODING_PROPERTY)).isEqualTo("Shift_JIS");
-  }
-
-  private MavenProject loadPom(String pomPath, boolean isRoot) throws URISyntaxException, IOException, XmlPullParserException {
-    File pomFile = new File(getClass().getResource(pomPath).toURI());
-    Model model = new MavenXpp3Reader().read(new StringReader(FileUtils.readFileToString(pomFile)));
-    MavenProject pom = new MavenProject(model);
-    pom.setFile(pomFile);
-    pom.getBuild().setDirectory("target");
-    pom.setExecutionRoot(isRoot);
-    return pom;
-  }
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/SonarMavenProjectBuilderTest.java b/plugins/sonar-maven-batch-plugin/src/test/java/org/sonar/plugins/maven/SonarMavenProjectBuilderTest.java
deleted file mode 100644 (file)
index a39798c..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.plugins.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class SonarMavenProjectBuilderTest {
-
-  @Test
-  public void testSimpleProject() {
-    MavenSession session = mock(MavenSession.class);
-    MavenProject rootProject = mock(MavenProject.class);
-    when(rootProject.isExecutionRoot()).thenReturn(true);
-    when(session.getSortedProjects()).thenReturn(Arrays.asList(rootProject));
-
-    MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
-    ProjectDefinition projectDefinition = ProjectDefinition.create();
-    when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
-    MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
-
-    assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
-
-    ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
-    verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
-    assertThat(argument.getValue()).contains(rootProject);
-  }
-
-  @Test
-  public void testMultimoduleProject() {
-    MavenSession session = mock(MavenSession.class);
-    MavenProject rootProject = mock(MavenProject.class);
-    MavenProject module1 = mock(MavenProject.class);
-    MavenProject module2 = mock(MavenProject.class);
-    when(rootProject.isExecutionRoot()).thenReturn(true);
-    when(module1.isExecutionRoot()).thenReturn(false);
-    when(module2.isExecutionRoot()).thenReturn(false);
-    when(session.getSortedProjects()).thenReturn(Arrays.asList(module1, module2, rootProject));
-
-    MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
-    ProjectDefinition projectDefinition = ProjectDefinition.create();
-    when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
-    MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
-
-    assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
-
-    ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
-    verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
-    assertThat(argument.getValue()).contains(module1, module2, rootProject);
-  }
-
-}
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom.xml
deleted file mode 100644 (file)
index 7b49562..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.test</groupId>
-    <artifactId>parent</artifactId>
-    <version>0.1-SNAPSHOT</version>
-  </parent>
-  <artifactId>module</artifactId>
-  <packaging>jar</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml
deleted file mode 100644 (file)
index 7b49562..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.test</groupId>
-    <artifactId>parent</artifactId>
-    <version>0.1-SNAPSHOT</version>
-  </parent>
-  <artifactId>module</artifactId>
-  <packaging>jar</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml
deleted file mode 100644 (file)
index 9d2244e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-  <modules>
-    <module>module</module>
-  </modules>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml
deleted file mode 100644 (file)
index 470f2d1..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.test</groupId>
-    <artifactId>parent</artifactId>
-    <version>0.1-SNAPSHOT</version>
-  </parent>
-  <artifactId>module1</artifactId>
-  <packaging>jar</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml
deleted file mode 100644 (file)
index 8810167..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.test</groupId>
-    <artifactId>parent</artifactId>
-    <version>0.1-SNAPSHOT</version>
-  </parent>
-  <artifactId>module2</artifactId>
-  <packaging>jar</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml
deleted file mode 100644 (file)
index afd92c0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-  <modules>
-    <module>path1</module>
-    <module>path2</module>
-  </modules>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml
deleted file mode 100644 (file)
index 470f2d1..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.test</groupId>
-    <artifactId>parent</artifactId>
-    <version>0.1-SNAPSHOT</version>
-  </parent>
-  <artifactId>module1</artifactId>
-  <packaging>jar</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml
deleted file mode 100644 (file)
index 8810167..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.test</groupId>
-    <artifactId>parent</artifactId>
-    <version>0.1-SNAPSHOT</version>
-  </parent>
-  <artifactId>module2</artifactId>
-  <packaging>jar</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml
deleted file mode 100644 (file)
index cc73a43..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-  <modules>
-    <module>module1</module>
-    <module>module2</module>
-  </modules>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinks/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinks/pom.xml
deleted file mode 100644 (file)
index 460e896..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-  <url>http://home.com</url>
-  <ciManagement>
-    <url>http://ci.com</url>
-  </ciManagement>
-  <issueManagement>
-    <url>http://issues.com</url>
-  </issueManagement>
-  <scm>
-    <url>http://sources.com</url>
-    <developerConnection>http://sources-dev.com</developerConnection>
-  </scm>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml
deleted file mode 100644 (file)
index 5b024e5..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-  <url>http://home.com</url>
-  <ciManagement>
-    <url>http://ci.com</url>
-  </ciManagement>
-  <issueManagement>
-    <url>http://issues.com</url>
-  </issueManagement>
-  <scm>
-    <url>http://sources.com</url>
-    <developerConnection>http://sources-dev.com</developerConnection>
-  </scm>
-
-
-
-  <properties>
-    <sonar.links.homepage>http://home.com-OVERRIDEN-BY-PROPS</sonar.links.homepage>
-    <sonar.links.scm>http://sources.com-OVERRIDEN-BY-PROPS</sonar.links.scm>
-  </properties>
-
-
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml
deleted file mode 100644 (file)
index ffd4053..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-</project>
\ No newline at end of file
diff --git a/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/sourceEncoding/pom.xml b/plugins/sonar-maven-batch-plugin/src/test/resources/org/sonar/plugins/maven/MavenProjectConverterTest/sourceEncoding/pom.xml
deleted file mode 100644 (file)
index 01bf73c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.test</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>pom</packaging>
-  <url>http://home.com</url>
-  <properties>
-    <project.build.sourceEncoding>Shift_JIS</project.build.sourceEncoding>
-  </properties>
-</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 62b3fcaea10090f2757ea159449358c28886a471..2d8029ed3923965a05934803688f24b990fe0a6f 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,6 @@
     <module>plugins/sonar-cpd-plugin</module>
     <module>plugins/sonar-design-plugin</module>
     <module>plugins/sonar-email-notifications-plugin</module>
-    <module>plugins/sonar-maven-batch-plugin</module>
     <module>plugins/sonar-xoo-plugin</module>
   </modules>
 
index d99cf5fedb4d0d7ae3b8e2f5fcedbd036721e1c5..6bdb789c059722c049144b6611190cf322c2196f 100644 (file)
         <exclude>net.sourceforge.jtds:jtds</exclude>
         <exclude>org.codehaus.sonar.plugins:*</exclude>
         <exclude>org.codehaus.sonar-plugins.*:*</exclude>
+        <exclude>org.codehaus.sonar:sonar-batch-maven-compat</exclude>
       </excludes>
     </dependencySet>
+    <dependencySet>
+      <outputDirectory>lib/batch</outputDirectory>
+      <useTransitiveDependencies>false</useTransitiveDependencies>
+      <includes>
+        <include>org.codehaus.sonar:sonar-batch-maven-compat</include>
+      </includes>
+    </dependencySet>
     <!-- Plugins -->
     <dependencySet>
       <outputDirectory>lib/core-plugins</outputDirectory>
index c59caf02c7b25b08de476181f3ff263caaeee37d..beb6302868a1bd83d94dbd9e9d207a32b15d21d2 100644 (file)
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>sonar-batch-maven-compat</artifactId>
+      <version>${pom.version}</version>
+      <scope>runtime</scope>
+    </dependency>
     <dependency>
       <groupId>org.apache.tomcat.embed</groupId>
       <artifactId>tomcat-embed-core</artifactId>
       <type>sonar-plugin</type>
       <scope>runtime</scope>
     </dependency>
-    <dependency>
-      <groupId>org.codehaus.sonar.plugins</groupId>
-      <artifactId>sonar-maven-batch-plugin</artifactId>
-      <version>${project.version}</version>
-      <type>sonar-plugin</type>
-      <scope>runtime</scope>
-    </dependency>
     <dependency>
       <groupId>org.sonatype.jsw-binaries</groupId>
       <artifactId>jsw-binaries</artifactId>
             <configuration>
               <rules>
                 <requireFilesSize>
-                  <minsize>55000000</minsize>
-                  <maxsize>75000000</maxsize>
+                  <minsize>80000000</minsize>
+                  <maxsize>88000000</maxsize>
                   <files>
                     <file>${project.build.directory}/sonarqube-${project.version}.zip</file>
                   </files>
index 3990762b4805db0df12d89393b13cdeb9374f175..087be6a8d573e4719855b89f257f385593c29cd7 100644 (file)
   <description>Compatibility layer, which provides MavenProject for non-Maven environments.</description>
 
   <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>sonar-batch</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-project</artifactId>
       <version>2.0.7</version>
-      <scope>compile</scope>
       <exclusions>
         <exclusion>
           <groupId>org.codehaus.plexus</groupId>
       </plugin>
     </plugins>
   </build>
-
-  <profiles>
-    <profile>
-      <id>release</id>
-      <build>
-        <plugins>
-          <plugin>
-            <artifactId>maven-deploy-plugin</artifactId>
-            <configuration>
-              <skip>true</skip>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
-    </profile>
-  </profiles>
 </project>
index 4d473f381496019bbf67aec034c2dbfbe639cb5e..caeffd62bcd3219775ca4b14cef3eee177035aaf 100644 (file)
@@ -7,7 +7,6 @@
     <version>4.3-SNAPSHOT</version>
   </parent>
 
-  <groupId>org.codehaus.sonar</groupId>
   <artifactId>sonar-batch</artifactId>
   <name>SonarQube :: Batch</name>
 
     <dependency>
       <groupId>org.codehaus.sonar</groupId>
       <artifactId>sonar-plugin-api</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>jfree</groupId>
+          <artifactId>jcommon</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>jfree</groupId>
+          <artifactId>jfreechat</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.codehaus.sonar</groupId>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-plugin-api</artifactId>
+      <version>3.0</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-core</artifactId>
+      <version>3.0</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
new file mode 100644 (file)
index 0000000..02c9855
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.Lists;
+import org.sonar.batch.debt.DebtDecorator;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.debt.NewDebtDecorator;
+import org.sonar.batch.maven.DefaultMavenPluginExecutor;
+import org.sonar.batch.maven.MavenProjectBootstrapper;
+import org.sonar.batch.maven.MavenProjectBuilder;
+import org.sonar.batch.maven.MavenProjectConverter;
+import org.sonar.core.config.CorePropertyDefinitions;
+
+import java.util.Collection;
+import java.util.List;
+
+public class BatchComponents {
+  public static Collection all() {
+    List components = Lists.newArrayList(
+      // Maven
+      MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class,
+
+      // Debt
+      IssueChangelogDebtCalculator.class, DebtDecorator.class, NewDebtDecorator.class
+    );
+    components.addAll(CorePropertyDefinitions.all());
+    return components;
+  }
+}
index 039cc1c210c999511bbd3d488f6c51db3b69ec26..bb2b2e431f2a0d6e105f383795022cb780688c6d 100644 (file)
@@ -44,6 +44,13 @@ public class ExtensionInstaller {
 
   public ExtensionInstaller install(ComponentContainer container, ExtensionMatcher matcher) {
     boolean preview = analysisMode.isPreview();
+
+    // core components
+    for (Object o : BatchComponents.all()) {
+      doInstall(container, matcher, null, preview, o);
+    }
+
+    // plugin extensions
     for (Map.Entry<PluginMetadata, Plugin> entry : pluginRepository.getPluginsByMetadata().entrySet()) {
       PluginMetadata metadata = entry.getKey();
       Plugin plugin = entry.getValue();
index c5992b6475a83f357d2132304280d44d64249cf7..d823a0650cfedaba53f794951dbbc0bd7182b7a7 100644 (file)
@@ -24,6 +24,7 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskComponent;
 import org.sonar.api.task.TaskDefinition;
 import org.sonar.api.task.TaskExtension;
 import org.sonar.api.utils.SonarException;
@@ -55,7 +56,7 @@ public class TaskContainer extends ComponentContainer {
   private void installTaskExtensions() {
     getComponentByType(ExtensionInstaller.class).install(this, new ExtensionMatcher() {
       public boolean accept(Object extension) {
-        return ExtensionUtils.isType(extension, TaskExtension.class);
+        return ExtensionUtils.isType(extension, TaskComponent.class);
       }
     });
   }
index 88de3031103cb854944d07c6ab4506d570d1d4cd..541a63a6bd7792498d3ac37d63dc6c5c7d8b42e7 100644 (file)
 package org.sonar.batch.components;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchExtension;
 import org.sonar.api.CoreProperties;
-import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.config.Settings;
 import org.sonar.api.database.model.Snapshot;
-import org.sonar.api.resources.Qualifiers;
 
 import javax.annotation.Nullable;
-
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
-import java.util.List;
 
 public class PastSnapshotFinder implements BatchExtension {
 
@@ -196,60 +191,4 @@ public class PastSnapshotFinder implements BatchExtension {
     }
   }
 
-  public static List<PropertyDefinition> getPropertyDefinitions() {
-    return ImmutableList.of(
-      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 1)
-        .name("Period 1")
-        .description("Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
-          "analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
-          "compare to previous analysis</li><li>'previous_version' to compare to the previous version in the project history</li></ul>" +
-          "<p>When specifying a number of days or a date, the snapshot selected for comparison is " +
-          " the first one available inside the corresponding time range. </p>" +
-          "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
-        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1)
-        .category(CoreProperties.CATEGORY_GENERAL)
-        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 2)
-        .name("Period 2")
-        .description("See the property 'Period 1'")
-        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2)
-        .category(CoreProperties.CATEGORY_GENERAL)
-        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 3)
-        .name("Period 3")
-        .description("See the property 'Period 1'")
-        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3)
-        .category(CoreProperties.CATEGORY_GENERAL)
-        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 4)
-        .name("Period 4")
-        .description("Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
-          "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
-          "for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li>" +
-          "<li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example 1.2</li></ul>" +
-          "<p>When specifying a number of days or a date, the snapshot selected for comparison is the first one available inside the corresponding time range. </p>" +
-          "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
-        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4)
-        .onlyOnQualifiers(Qualifiers.PROJECT)
-        .category(CoreProperties.CATEGORY_GENERAL)
-        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
-        .build(),
-
-      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 5)
-        .name("Period 5")
-        .description("See the property 'Period 4'")
-        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5)
-        .onlyOnQualifiers(Qualifiers.PROJECT)
-        .category(CoreProperties.CATEGORY_GENERAL)
-        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
-        .build()
-    );
-  }
-
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java
new file mode 100644 (file)
index 0000000..71e9cc3
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.batch.debt;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilters;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.technicaldebt.batch.Characteristic;
+import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+
+/**
+ * Decorator that computes the technical debt metric
+ */
+@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
+public final class DebtDecorator implements Decorator {
+
+  private final ResourcePerspectives perspectives;
+  private final TechnicalDebtModel model;
+  private final Rules rules;
+
+  /**
+   * ruleFinder is needed to load "old" rule in order to persist rule measure
+   */
+  private final RuleFinder ruleFinder;
+
+  public DebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules, RuleFinder ruleFinder) {
+    this.perspectives = perspectives;
+    this.model = model;
+    this.rules = rules;
+    this.ruleFinder = ruleFinder;
+  }
+
+  public boolean shouldExecuteOnProject(Project project) {
+    return true;
+  }
+
+  @DependedUpon
+  public List<Metric> generatesMetrics() {
+    return Arrays.asList(CoreMetrics.TECHNICAL_DEBT);
+  }
+
+  public void decorate(Resource resource, DecoratorContext context) {
+    Issuable issuable = perspectives.as(Issuable.class, resource);
+    if (issuable != null && shouldSaveMeasure(context)) {
+      List<Issue> issues = newArrayList(issuable.issues());
+      saveMeasures(context, issues);
+    }
+  }
+
+  private void saveMeasures(DecoratorContext context, List<Issue> issues) {
+    Long total = 0L;
+    SumMap<RuleKey> ruleDebts = new SumMap<RuleKey>();
+    SumMap<Characteristic> characteristicDebts = new SumMap<Characteristic>();
+
+    // Aggregate rules debt from current issues (and populate current characteristic debt)
+    for (Issue issue : issues) {
+      Long debt = ((DefaultIssue) issue).debtInMinutes();
+      total += computeDebt(debt, issue.ruleKey(), ruleDebts, characteristicDebts);
+    }
+
+    // Aggregate rules debt from children (and populate children characteristics debt)
+    for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) {
+      Long debt = measure.getValue().longValue();
+      RuleMeasure ruleMeasure = (RuleMeasure) measure;
+      total += computeDebt(debt, ruleMeasure.getRule().ruleKey(), ruleDebts, characteristicDebts);
+    }
+
+    context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue());
+    saveOnRule(context, ruleDebts);
+    for (Characteristic characteristic : model.characteristics()) {
+      Long debt = characteristicDebts.get(characteristic);
+      saveCharacteristicMeasure(context, characteristic, debt != null ? debt.doubleValue() : 0d, false);
+    }
+  }
+
+  private Long computeDebt(@Nullable Long debt, RuleKey ruleKey, SumMap<RuleKey> ruleDebts, SumMap<Characteristic> characteristicDebts) {
+    if (debt != null) {
+      Rule rule = rules.find(ruleKey);
+      if (rule != null) {
+        String characteristicKey = rule.debtSubCharacteristic();
+        if (characteristicKey != null) {
+          Characteristic characteristic = model.characteristicByKey(characteristicKey);
+          if (characteristic != null) {
+            ruleDebts.add(ruleKey, debt);
+            characteristicDebts.add(characteristic, debt);
+            propagateTechnicalDebtInParents(characteristic.parent(), debt, characteristicDebts);
+            return debt;
+          }
+        }
+      }
+    }
+    return 0L;
+  }
+
+  private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, long value, SumMap<Characteristic> characteristicDebts) {
+    if (characteristic != null) {
+      characteristicDebts.add(characteristic, value);
+      propagateTechnicalDebtInParents(characteristic.parent(), value, characteristicDebts);
+    }
+  }
+
+  private void saveOnRule(DecoratorContext context, SumMap<RuleKey> ruleDebts) {
+    for (Map.Entry<RuleKey, Long> entry : ruleDebts.entrySet()) {
+      org.sonar.api.rules.Rule oldRule = ruleFinder.findByKey(entry.getKey());
+      if (oldRule != null) {
+        saveRuleMeasure(context, oldRule, entry.getValue().doubleValue(), ResourceUtils.isEntity(context.getResource()));
+      }
+    }
+  }
+
+  @VisibleForTesting
+  void saveCharacteristicMeasure(DecoratorContext context, Characteristic characteristic, Double value, boolean inMemory) {
+    // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
+    // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
+    if (value > 0.0 || (ResourceUtils.isProject(context.getResource()) && characteristic.isRoot())) {
+      Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT);
+      measure.setCharacteristic(characteristic);
+      saveMeasure(context, measure, value, inMemory);
+    }
+  }
+
+  @VisibleForTesting
+  void saveRuleMeasure(DecoratorContext context, org.sonar.api.rules.Rule rule, Double value, boolean inMemory) {
+    // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
+    // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
+    if (value > 0.0) {
+      RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, rule, null, null);
+      saveMeasure(context, measure, value, inMemory);
+    }
+  }
+
+  private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) {
+    measure.setValue(value);
+    if (inMemory) {
+      measure.setPersistenceMode(PersistenceMode.MEMORY);
+    }
+    context.saveMeasure(measure);
+  }
+
+  private boolean shouldSaveMeasure(DecoratorContext context) {
+    return context.getMeasure(CoreMetrics.TECHNICAL_DEBT) == null;
+  }
+
+  private static class SumMap<E> {
+    private Map<E, Long> sumByKeys;
+
+    public SumMap() {
+      sumByKeys = newHashMap();
+    }
+
+    public void add(@Nullable E key, Long value) {
+      if (key != null) {
+        Long currentValue = sumByKeys.get(key);
+        sumByKeys.put(key, currentValue != null ? currentValue + value : value);
+      }
+    }
+
+    @CheckForNull
+    public Long get(E key) {
+      return sumByKeys.get(key);
+    }
+
+    public Set<Map.Entry<E, Long>> entrySet() {
+      return sumByKeys.entrySet();
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/NewDebtDecorator.java
new file mode 100644 (file)
index 0000000..fe738d6
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.batch.debt;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.batch.*;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.components.Period;
+import org.sonar.batch.components.TimeMachineConfiguration;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Decorator that computes the technical debt metric
+ */
+@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
+public final class NewDebtDecorator implements Decorator {
+
+  private final ResourcePerspectives perspectives;
+  private final TimeMachineConfiguration timeMachineConfiguration;
+  private final IssueChangelogDebtCalculator issueChangelogDebtCalculator;
+
+  public NewDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration,
+                          IssueChangelogDebtCalculator issueChangelogDebtCalculator) {
+    this.perspectives = perspectives;
+    this.timeMachineConfiguration = timeMachineConfiguration;
+    this.issueChangelogDebtCalculator = issueChangelogDebtCalculator;
+  }
+
+  public boolean shouldExecuteOnProject(Project project) {
+    return true;
+  }
+
+  @DependedUpon
+  public List<Metric> generatesMetrics() {
+    return ImmutableList.of(
+      CoreMetrics.NEW_TECHNICAL_DEBT
+    );
+  }
+
+  public void decorate(Resource resource, DecoratorContext context) {
+    Issuable issuable = perspectives.as(Issuable.class, resource);
+    if (issuable != null && shouldSaveNewMetrics(context)) {
+      List<Issue> issues = newArrayList(issuable.issues());
+      saveMeasures(context, issues);
+    }
+  }
+
+  private void saveMeasures(DecoratorContext context, Collection<Issue> issues) {
+    Measure measure = new Measure(CoreMetrics.NEW_TECHNICAL_DEBT);
+    for (Period period : timeMachineConfiguration.periods()) {
+      Date periodDate = period.getDate();
+      double value = calculateNewTechnicalDebtValue(issues, periodDate);
+      Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
+      double sum = MeasureUtils.sumOnVariation(true, period.getIndex(), children) + value;
+      measure.setVariation(period.getIndex(), sum);
+    }
+    context.saveMeasure(measure);
+  }
+
+  private long calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) {
+    long result = 0;
+    for (Issue issue : issues) {
+      Long debt = issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, periodDate);
+      if (debt != null) {
+        result += debt;
+      }
+    }
+    return result;
+  }
+
+  private boolean shouldSaveNewMetrics(DecoratorContext context) {
+    return context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT) == null;
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ignore/IssueExclusionsConfiguration.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ignore/IssueExclusionsConfiguration.java
deleted file mode 100644 (file)
index 18c2f9f..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.batch.issue.ignore;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.config.PropertyFieldDefinition;
-import org.sonar.api.resources.Qualifiers;
-
-import java.util.List;
-
-public final class IssueExclusionsConfiguration {
-
-  public static final String SUB_CATEGORY_IGNORE_ISSUES = "issues";
-
-  public static final String EXCLUSION_KEY_PREFIX = "sonar.issue.ignore";
-  public static final String INCLUSION_KEY_PREFIX = "sonar.issue.enforce";
-
-  public static final String MULTICRITERIA_SUFFIX = ".multicriteria";
-  public static final String PATTERNS_MULTICRITERIA_EXCLUSION_KEY = EXCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
-  public static final String PATTERNS_MULTICRITERIA_INCLUSION_KEY = INCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
-  public static final String RESOURCE_KEY = "resourceKey";
-  private static final String PROPERTY_FILE_PATH_PATTERN = "File Path Pattern";
-  public static final String RULE_KEY = "ruleKey";
-  private static final String PROPERTY_RULE_KEY_PATTERN = "Rule Key Pattern";
-  private static final String PROPERTY_RULE_KEY_PATTERN_HELP = "<br/>A rule key pattern consists of the rule repository name, followed by a colon, followed by a rule key "
-    + "or rule name fragment. For example:"
-    + "<ul><li>squid:S1195</li><li>squid:*Naming*</li></ul>";
-
-  public static final String BLOCK_SUFFIX = ".block";
-  public static final String PATTERNS_BLOCK_KEY = EXCLUSION_KEY_PREFIX + BLOCK_SUFFIX;
-  public static final String BEGIN_BLOCK_REGEXP = "beginBlockRegexp";
-  public static final String END_BLOCK_REGEXP = "endBlockRegexp";
-
-  public static final String ALLFILE_SUFFIX = ".allfile";
-  public static final String PATTERNS_ALLFILE_KEY = EXCLUSION_KEY_PREFIX + ALLFILE_SUFFIX;
-  public static final String FILE_REGEXP = "fileRegexp";
-
-  static final int LARGE_SIZE = 40;
-  static final int SMALL_SIZE = 10;
-
-  private IssueExclusionsConfiguration() {
-    // static configuration declaration only
-  }
-
-  public static List<PropertyDefinition> getPropertyDefinitions() {
-    return ImmutableList.of(
-      PropertyDefinition.builder(PATTERNS_MULTICRITERIA_EXCLUSION_KEY)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
-        .name("Ignore Issues on Multiple Criteria")
-        .description("Patterns to ignore issues on certain components and for certain coding rules." + PROPERTY_RULE_KEY_PATTERN_HELP)
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(3)
-        .fields(
-          PropertyFieldDefinition.build(RULE_KEY)
-            .name(PROPERTY_RULE_KEY_PATTERN)
-            .description("Pattern to match rules which should be ignored.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build(),
-          PropertyFieldDefinition.build(RESOURCE_KEY)
-            .name(PROPERTY_FILE_PATH_PATTERN)
-            .description("Pattern to match files which should be ignored.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build())
-        .build(),
-      PropertyDefinition.builder(PATTERNS_BLOCK_KEY)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
-        .name("Ignore Issues in Blocks")
-        .description("Patterns to ignore all issues on specific blocks of code, while continuing to scan and mark issues on the remainder of the file.")
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(2)
-        .fields(
-          PropertyFieldDefinition.build(BEGIN_BLOCK_REGEXP)
-            .name("Regular Expression for Start of Block")
-            .description("If this regular expression is found in a file, then following lines are ignored until end of block.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build(),
-          PropertyFieldDefinition.build(END_BLOCK_REGEXP)
-            .name("Regular Expression for End of Block")
-            .description("If specified, this regular expression is used to determine the end of code blocks to ignore. If not, then block ends at the end of file.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build())
-        .build(),
-      PropertyDefinition.builder(PATTERNS_ALLFILE_KEY)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
-        .name("Ignore Issues on Files")
-        .description("Patterns to ignore all issues on files that contain a block of code matching a given regular expression.")
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(1)
-        .fields(
-          PropertyFieldDefinition.build(FILE_REGEXP)
-            .name("Regular Expression")
-            .description("If this regular expression is found in a file, then the whole file is ignored.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build())
-        .build(),
-      PropertyDefinition.builder(PATTERNS_MULTICRITERIA_INCLUSION_KEY)
-        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
-        .name("Restrict Scope of Coding Rules")
-        .description("Patterns to restrict the application of a rule to only certain components, ignoring all others." + PROPERTY_RULE_KEY_PATTERN_HELP)
-        .onQualifiers(Qualifiers.PROJECT)
-        .index(4)
-        .fields(
-          PropertyFieldDefinition.build(RULE_KEY)
-            .name(PROPERTY_RULE_KEY_PATTERN)
-            .description("Pattern used to match rules which should be restricted.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build(),
-          PropertyFieldDefinition.build(RESOURCE_KEY)
-            .name(PROPERTY_FILE_PATH_PATTERN)
-            .description("Pattern used to match files to which the rules should be restricted.")
-            .type(PropertyType.STRING)
-            .indicativeSize(LARGE_SIZE)
-            .build())
-        .build());
-  }
-}
index 03d218b3a3b7b400225b1a16238b789e0cd205e0..e29fc5aaa30661829dfa6304a2d8fb8aace997ea 100644 (file)
@@ -19,8 +19,6 @@
  */
 package org.sonar.batch.issue.ignore.pattern;
 
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
@@ -67,8 +65,8 @@ public abstract class AbstractPatternInitializer implements BatchExtension {
     String patternConf = StringUtils.defaultIfBlank(settings.getString(getMulticriteriaConfigurationKey()), "");
     for (String id : StringUtils.split(patternConf, ',')) {
       String propPrefix = getMulticriteriaConfigurationKey() + "." + id + ".";
-      String resourceKeyPattern = settings.getString(propPrefix + IssueExclusionsConfiguration.RESOURCE_KEY);
-      String ruleKeyPattern = settings.getString(propPrefix + IssueExclusionsConfiguration.RULE_KEY);
+      String resourceKeyPattern = settings.getString(propPrefix + "resourceKey");
+      String ruleKeyPattern = settings.getString(propPrefix + "ruleKey");
       String lineRange = "*";
       String[] fields = new String[] { resourceKeyPattern, ruleKeyPattern, lineRange };
       PatternDecoder.checkRegularLineConstraints(StringUtils.join(fields, ","), fields);
index 79b176b47212dd6a76cb6db2654695b56bc78ae4..75882be5422cb916afa6758b641862b419943711 100644 (file)
 
 package org.sonar.batch.issue.ignore.pattern;
 
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.config.Settings;
+import org.sonar.core.config.IssueExclusionProperties;
 
 import java.util.List;
 
@@ -45,7 +44,7 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer
 
   @Override
   protected String getMulticriteriaConfigurationKey() {
-    return IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY;
+    return "sonar.issue.ignore" + ".multicriteria";
   }
 
   public PatternMatcher getPatternMatcher() {
@@ -54,7 +53,7 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer
 
   @Override
   public void initializePatternsForPath(String relativePath, String componentKey) {
-    for (IssuePattern pattern: getMulticriteriaPatterns()) {
+    for (IssuePattern pattern : getMulticriteriaPatterns()) {
       if (pattern.matchResource(relativePath)) {
         getPatternMatcher().addPatternForComponent(componentKey, pattern);
       }
@@ -70,12 +69,12 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer
   protected final void loadFileContentPatterns() {
     // Patterns Block
     blockPatterns = Lists.newArrayList();
-    String patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY), "");
+    String patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionProperties.PATTERNS_BLOCK_KEY), "");
     for (String id : StringUtils.split(patternConf, ',')) {
-      String propPrefix = IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + "." + id + ".";
-      String beginBlockRegexp = getSettings().getString(propPrefix + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP);
-      String endBlockRegexp = getSettings().getString(propPrefix + IssueExclusionsConfiguration.END_BLOCK_REGEXP);
-      String[] fields = new String[] { beginBlockRegexp, endBlockRegexp };
+      String propPrefix = IssueExclusionProperties.PATTERNS_BLOCK_KEY + "." + id + ".";
+      String beginBlockRegexp = getSettings().getString(propPrefix + IssueExclusionProperties.BEGIN_BLOCK_REGEXP);
+      String endBlockRegexp = getSettings().getString(propPrefix + IssueExclusionProperties.END_BLOCK_REGEXP);
+      String[] fields = new String[]{beginBlockRegexp, endBlockRegexp};
       PatternDecoder.checkDoubleRegexpLineConstraints(StringUtils.join(fields, ","), fields);
       IssuePattern pattern = new IssuePattern().setBeginBlockRegexp(nullToEmpty(beginBlockRegexp)).setEndBlockRegexp(nullToEmpty(endBlockRegexp));
       blockPatterns.add(pattern);
@@ -83,10 +82,10 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer
 
     // Patterns All File
     allFilePatterns = Lists.newArrayList();
-    patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY), "");
+    patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionProperties.PATTERNS_ALLFILE_KEY), "");
     for (String id : StringUtils.split(patternConf, ',')) {
-      String propPrefix = IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + "." + id + ".";
-      String allFileRegexp = getSettings().getString(propPrefix + IssueExclusionsConfiguration.FILE_REGEXP);
+      String propPrefix = IssueExclusionProperties.PATTERNS_ALLFILE_KEY + "." + id + ".";
+      String allFileRegexp = getSettings().getString(propPrefix + IssueExclusionProperties.FILE_REGEXP);
       PatternDecoder.checkWholeFileRegexp(allFileRegexp);
       IssuePattern pattern = new IssuePattern().setAllFileRegexp(nullToEmpty(allFileRegexp));
       allFilePatterns.add(pattern);
@@ -102,6 +101,6 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer
   }
 
   public boolean hasFileContentPattern() {
-    return ! (blockPatterns.isEmpty() && allFilePatterns.isEmpty());
+    return !(blockPatterns.isEmpty() && allFilePatterns.isEmpty());
   }
 }
index 5ce390af0be8fec9b09d17e1ab3de6388ea51623..bf87ad85313bf3e0f843f5d3b6e42b2c5f9b9430 100644 (file)
@@ -20,8 +20,6 @@
 
 package org.sonar.batch.issue.ignore.pattern;
 
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
 import com.google.common.collect.Maps;
 import org.sonar.api.config.Settings;
 
@@ -38,7 +36,7 @@ public class IssueInclusionPatternInitializer extends AbstractPatternInitializer
 
   @Override
   protected String getMulticriteriaConfigurationKey() {
-    return IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY;
+    return "sonar.issue.enforce" + ".multicriteria";
   }
 
   @Override
diff --git a/sonar-batch/src/main/java/org/sonar/batch/maven/DefaultMavenPluginExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/maven/DefaultMavenPluginExecutor.java
new file mode 100644 (file)
index 0000000..fb0a1a5
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.ReactorManager;
+import org.apache.maven.lifecycle.LifecycleExecutor;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.maven.MavenPlugin;
+import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.TimeProfiler;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.maven.MavenPluginExecutor;
+
+import javax.annotation.Nullable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+@SupportedEnvironment("maven")
+public class DefaultMavenPluginExecutor implements MavenPluginExecutor {
+
+  private final MavenProjectConverter pomConverter;
+  private LifecycleExecutor lifecycleExecutor;
+  private MavenSession mavenSession;
+
+  public DefaultMavenPluginExecutor(LifecycleExecutor le, MavenSession mavenSession, MavenProjectConverter pomConverter) {
+    this.lifecycleExecutor = le;
+    this.mavenSession = mavenSession;
+    this.pomConverter = pomConverter;
+  }
+
+  @Override
+  public final MavenPluginHandler execute(Project project, DefaultModuleFileSystem fs, MavenPluginHandler handler) {
+    for (String goal : handler.getGoals()) {
+      if (goal == null) {
+        throw new IllegalStateException("Maven goal can't be null");
+      }
+      MavenPlugin plugin = MavenPlugin.getPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId());
+      execute(project,
+        fs,
+        getGoal(handler.getGroupId(), handler.getArtifactId(), plugin != null && plugin.getPlugin() != null ? plugin.getPlugin().getVersion() : null, goal));
+    }
+    return handler;
+  }
+
+  @Override
+  public final void execute(Project project, DefaultModuleFileSystem fs, String goal) {
+    if (project.getPom() != null) {
+      TimeProfiler profiler = new TimeProfiler().start("Execute " + goal);
+      ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+      try {
+        concreteExecute(project.getPom(), goal);
+      } catch (Exception e) {
+        throw new SonarException("Unable to execute maven plugin", e);
+      } finally {
+        // Reset original ClassLoader that may have been changed during Maven Execution (see SONAR-1800)
+        Thread.currentThread().setContextClassLoader(currentClassLoader);
+        profiler.stop();
+      }
+      if (!fs.isInitialized()) {
+        pomConverter.synchronizeFileSystem(project.getPom(), fs);
+      }
+    }
+  }
+
+  static String getGoal(String groupId, String artifactId, @Nullable String version, String goal) {
+    String defaultVersion = version == null ? "" : version;
+    return new StringBuilder()
+      .append(groupId).append(":")
+      .append(artifactId).append(":")
+      .append(defaultVersion)
+      .append(":")
+      .append(goal)
+      .toString();
+  }
+
+  public void concreteExecute(MavenProject pom, String goal) {
+    Method executeMethod = null;
+    for (Method m : lifecycleExecutor.getClass().getMethods()) {
+      if ("execute".equals(m.getName())) {
+        executeMethod = m;
+        break;
+      }
+    }
+    if (executeMethod == null) {
+      throw new SonarException("Unable to find execute method on Maven LifecycleExecutor. Please check your Maven version.");
+    }
+    if (executeMethod.getParameterTypes().length == 1) {
+      concreteExecuteMaven3(pom, goal);
+    } else if (executeMethod.getParameterTypes().length == 3) {
+      concreteExecuteMaven2(executeMethod, pom, goal);
+    } else {
+      throw new SonarException("Unexpected parameter count on Maven LifecycleExecutor#execute method. Please check your Maven version.");
+    }
+  }
+
+  public void concreteExecuteMaven3(MavenProject pom, String goal) {
+    MavenSession projectSession = mavenSession.clone();
+    projectSession.setCurrentProject(pom);
+    projectSession.setProjects(Arrays.asList(pom));
+    projectSession.getRequest().setRecursive(false);
+    projectSession.getRequest().setPom(pom.getFile());
+    projectSession.getRequest().setGoals(Arrays.asList(goal));
+    projectSession.getRequest().setInteractiveMode(false);
+    lifecycleExecutor.execute(projectSession);
+    if (projectSession.getResult().hasExceptions()) {
+      throw new SonarException("Exception during execution of " + goal);
+    }
+  }
+
+  public void concreteExecuteMaven2(Method executeMethod, MavenProject pom, String goal) {
+    try {
+      ReactorManager reactor = new ReactorManager(Arrays.asList(pom));
+      MavenSession clonedSession = new MavenSession(mavenSession.getContainer(),
+        mavenSession.getSettings(),
+        mavenSession.getLocalRepository(),
+        mavenSession.getEventDispatcher(),
+        reactor,
+        Arrays.asList(goal),
+        mavenSession.getExecutionRootDirectory(),
+        mavenSession.getExecutionProperties(),
+        mavenSession.getStartTime());
+      executeMethod.invoke(lifecycleExecutor, clonedSession, reactor, clonedSession.getEventDispatcher());
+    } catch (Exception e) {
+      throw new SonarException("Unable to execute Maven 2 plugin", e);
+    }
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectBootstrapper.java b/sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectBootstrapper.java
new file mode 100644 (file)
index 0000000..3cd1f9d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import java.util.List;
+
+/**
+ * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
+ */
+@Deprecated
+@SupportedEnvironment("maven")
+public class MavenProjectBootstrapper implements ProjectBootstrapper {
+
+  private MavenSession session;
+  private MavenProjectConverter mavenProjectConverter;
+
+  public MavenProjectBootstrapper(MavenSession session, MavenProjectConverter mavenProjectConverter) {
+    this.session = session;
+    this.mavenProjectConverter = mavenProjectConverter;
+  }
+
+  @Override
+  public ProjectReactor bootstrap() {
+    // Don't use session.getTopLevelProject or session.getProjects to keep compatibility with Maven 2
+    List<MavenProject> sortedProjects = session.getSortedProjects();
+    MavenProject topLevelProject = null;
+    for (MavenProject project : sortedProjects) {
+      if (project.isExecutionRoot()) {
+        topLevelProject = project;
+        break;
+      }
+    }
+    if (topLevelProject == null) {
+      throw new IllegalStateException("Maven session does not declare a top level project");
+    }
+    return new ProjectReactor(mavenProjectConverter.configure(sortedProjects, topLevelProject));
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectBuilder.java
new file mode 100644 (file)
index 0000000..7cbf3ef
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.bootstrap.ProjectBuilder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import java.util.List;
+
+/**
+ * Class that inject MavenProject in each module container
+ */
+@SupportedEnvironment("maven")
+public class MavenProjectBuilder extends ProjectBuilder {
+
+  private final MavenSession mavenSession;
+
+  public MavenProjectBuilder(MavenSession mavenSession) {
+    this.mavenSession = mavenSession;
+  }
+
+  @Override
+  public void build(Context context) {
+    ProjectReactor reactor = context.projectReactor();
+    for (ProjectDefinition moduleDef : reactor.getProjects()) {
+      setMavenProjectIfApplicable(moduleDef);
+    }
+  }
+
+  private void setMavenProjectIfApplicable(ProjectDefinition definition) {
+    if (mavenSession != null) {
+      String moduleKey = definition.getKey();
+      for (MavenProject mavenModule : (List<MavenProject>) mavenSession.getSortedProjects()) {
+        String mavenModuleKey = mavenModule.getGroupId() + ":" + mavenModule.getArtifactId();
+        if (mavenModuleKey.equals(moduleKey) && !definition.getContainerExtensions().contains(mavenModule)) {
+          definition.addContainerExtension(mavenModule);
+        }
+      }
+    }
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectConverter.java b/sonar-batch/src/main/java/org/sonar/batch/maven/MavenProjectConverter.java
new file mode 100644 (file)
index 0000000..2ef3fe6
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.model.CiManagement;
+import org.apache.maven.model.IssueManagement;
+import org.apache.maven.model.Scm;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.maven.MavenUtils;
+import org.sonar.api.task.TaskExtension;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.java.api.JavaUtils;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
+ */
+@Deprecated
+@SupportedEnvironment("maven")
+public class MavenProjectConverter implements TaskExtension {
+
+  private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
+    " Probably you use Maven Advanced Reactor Options, which is not supported by SonarQube and should not be used.";
+
+  public ProjectDefinition configure(List<MavenProject> poms, MavenProject root) {
+    // projects by canonical path to pom.xml
+    Map<String, MavenProject> paths = Maps.newHashMap();
+    Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();
+
+    try {
+      configureModules(poms, paths, defs);
+
+      rebuildModuleHierarchy(paths, defs);
+    } catch (IOException e) {
+      throw new IllegalStateException("Cannot configure project", e);
+    }
+
+    ProjectDefinition rootProject = defs.get(root);
+    if (rootProject == null) {
+      throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
+    }
+    return rootProject;
+  }
+
+  private void rebuildModuleHierarchy(Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
+    for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
+      MavenProject pom = entry.getValue();
+      for (Object m : pom.getModules()) {
+        String moduleId = (String) m;
+        File modulePath = new File(pom.getBasedir(), moduleId);
+        MavenProject module = findMavenProject(modulePath, paths);
+
+        ProjectDefinition parentProject = defs.get(pom);
+        if (parentProject == null) {
+          throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
+        }
+        ProjectDefinition subProject = defs.get(module);
+        if (subProject == null) {
+          throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
+        }
+        parentProject.addSubProject(subProject);
+      }
+    }
+  }
+
+  private void configureModules(List<MavenProject> poms, Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
+    for (MavenProject pom : poms) {
+      paths.put(pom.getFile().getCanonicalPath(), pom);
+      ProjectDefinition def = ProjectDefinition.create();
+      merge(pom, def);
+      defs.put(pom, def);
+    }
+  }
+
+  private static MavenProject findMavenProject(final File modulePath, Map<String, MavenProject> paths) throws IOException {
+    if (modulePath.exists() && modulePath.isDirectory()) {
+      for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
+        String pomFileParentDir = new File(entry.getKey()).getParent();
+        if (pomFileParentDir.equals(modulePath.getCanonicalPath())) {
+          return entry.getValue();
+        }
+      }
+      return null;
+    }
+    return paths.get(modulePath.getCanonicalPath());
+  }
+
+  @VisibleForTesting
+  void merge(MavenProject pom, ProjectDefinition definition) {
+    String key = getSonarKey(pom);
+    // IMPORTANT NOTE : reference on properties from POM model must not be saved,
+    // instead they should be copied explicitly - see SONAR-2896
+    definition
+      .setProperties(pom.getModel().getProperties())
+      .setKey(key)
+      .setVersion(pom.getVersion())
+      .setName(pom.getName())
+      .setDescription(pom.getDescription())
+      .addContainerExtension(pom);
+    guessJavaVersion(pom, definition);
+    guessEncoding(pom, definition);
+    convertMavenLinksToProperties(definition, pom);
+    synchronizeFileSystem(pom, definition);
+  }
+
+  private static String getSonarKey(MavenProject pom) {
+    return new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
+  }
+
+  private static void guessEncoding(MavenProject pom, ProjectDefinition definition) {
+    // See http://jira.codehaus.org/browse/SONAR-2151
+    String encoding = MavenUtils.getSourceEncoding(pom);
+    if (encoding != null) {
+      definition.setProperty(CoreProperties.ENCODING_PROPERTY, encoding);
+    }
+  }
+
+  private static void guessJavaVersion(MavenProject pom, ProjectDefinition definition) {
+    // See http://jira.codehaus.org/browse/SONAR-2148
+    // Get Java source and target versions from maven-compiler-plugin.
+    String version = MavenUtils.getJavaSourceVersion(pom);
+    if (version != null) {
+      definition.setProperty(JavaUtils.JAVA_SOURCE_PROPERTY, version);
+    }
+    version = MavenUtils.getJavaVersion(pom);
+    if (version != null) {
+      definition.setProperty(JavaUtils.JAVA_TARGET_PROPERTY, version);
+    }
+  }
+
+  /**
+   * For SONAR-3676
+   */
+  private static void convertMavenLinksToProperties(ProjectDefinition definition, MavenProject pom) {
+    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_HOME_PAGE, pom.getUrl());
+
+    Scm scm = pom.getScm();
+    if (scm == null) {
+      scm = new Scm();
+    }
+    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES, scm.getUrl());
+    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES_DEV, scm.getDeveloperConnection());
+
+    CiManagement ci = pom.getCiManagement();
+    if (ci == null) {
+      ci = new CiManagement();
+    }
+    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_CI, ci.getUrl());
+
+    IssueManagement issues = pom.getIssueManagement();
+    if (issues == null) {
+      issues = new IssueManagement();
+    }
+    setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_ISSUE_TRACKER, issues.getUrl());
+  }
+
+  private static void setPropertyIfNotAlreadyExists(ProjectDefinition definition, String propertyKey, String propertyValue) {
+    if (StringUtils.isBlank(definition.getProperties().getProperty(propertyKey))) {
+      definition.setProperty(propertyKey, StringUtils.defaultString(propertyValue));
+    }
+  }
+
+  public void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
+    into.setBaseDir(pom.getBasedir());
+    File buildDir = getBuildDir(pom);
+    if (buildDir != null) {
+      into.setBuildDir(buildDir);
+      into.setWorkDir(getSonarWorkDir(pom));
+    }
+    into.setSourceDirs(toPaths(mainDirs(pom)));
+    into.setTestDirs(toPaths(testDirs(pom)));
+    File binaryDir = resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir());
+    if (binaryDir != null) {
+      into.addBinaryDir(binaryDir);
+    }
+  }
+
+  public static File getSonarWorkDir(MavenProject pom) {
+    return new File(getBuildDir(pom), "sonar");
+  }
+
+  private static File getBuildDir(MavenProject pom) {
+    return resolvePath(pom.getBuild().getDirectory(), pom.getBasedir());
+  }
+
+  public void synchronizeFileSystem(MavenProject pom, DefaultModuleFileSystem into) {
+    into.resetDirs(
+      pom.getBasedir(),
+      getBuildDir(pom),
+      mainDirs(pom),
+      testDirs(pom),
+      Arrays.asList(resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir())));
+  }
+
+  static File resolvePath(@Nullable String path, File basedir) {
+    if (path != null) {
+      File file = new File(StringUtils.trim(path));
+      if (!file.isAbsolute()) {
+        try {
+          file = new File(basedir, path).getCanonicalFile();
+        } catch (IOException e) {
+          throw new IllegalStateException("Unable to resolve path '" + path + "'", e);
+        }
+      }
+      return file;
+    }
+    return null;
+  }
+
+  static List<File> resolvePaths(List<String> paths, File basedir) {
+    List<File> result = Lists.newArrayList();
+    for (String path : paths) {
+      File dir = resolvePath(path, basedir);
+      if (dir != null) {
+        result.add(dir);
+      }
+    }
+    return result;
+  }
+
+  private List<File> mainDirs(MavenProject pom) {
+    return sourceDirs(pom, ProjectDefinition.SOURCE_DIRS_PROPERTY, pom.getCompileSourceRoots());
+  }
+
+  private List<File> testDirs(MavenProject pom) {
+    return sourceDirs(pom, ProjectDefinition.TEST_DIRS_PROPERTY, pom.getTestCompileSourceRoots());
+  }
+
+  private List<File> sourceDirs(MavenProject pom, String propertyKey, List mavenDirs) {
+    List<String> paths;
+    String prop = pom.getProperties().getProperty(propertyKey);
+    if (prop != null) {
+      paths = Arrays.asList(StringUtils.split(prop, ","));
+      // do not remove dirs that do not exist. They must be kept in order to
+      // notify users that value of sonar.sources has a typo.
+      return existingDirsOrFail(resolvePaths(paths, pom.getBasedir()), pom, propertyKey);
+    }
+
+    List<File> dirs = resolvePaths(mavenDirs, pom.getBasedir());
+
+    // Maven provides some directories that do not exist. They
+    // should be removed
+    return keepExistingDirs(dirs);
+  }
+
+  private List<File> existingDirsOrFail(List<File> dirs, MavenProject pom, String propertyKey) {
+    for (File dir : dirs) {
+      if (!dir.isDirectory() || !dir.exists()) {
+        throw MessageException.of(String.format(
+          "The directory '%s' does not exist for Maven module %s. Please check the property %s",
+          dir.getAbsolutePath(), pom.getId(), propertyKey));
+      }
+    }
+    return dirs;
+  }
+
+  private static List<File> keepExistingDirs(List<File> files) {
+    return Lists.newArrayList(Collections2.filter(files, new Predicate<File>() {
+      @Override
+      public boolean apply(File dir) {
+        return dir != null && dir.exists() && dir.isDirectory();
+      }
+    }));
+  }
+
+  private static String[] toPaths(Collection<File> dirs) {
+    Collection<String> paths = Collections2.transform(dirs, new Function<File, String>() {
+      @Override
+      public String apply(File dir) {
+        return dir.getAbsolutePath();
+      }
+    });
+    return paths.toArray(new String[paths.size()]);
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/maven/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/maven/package-info.java
new file mode 100644 (file)
index 0000000..b595cf0
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * This package is a part of bootstrap process, so we should take care about backward compatibility.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.maven;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
index 835aa9be91f1a7b1bf952449997643c36d400edb..5180a49875251ee07e08ae7c67ad511c06d39e9b 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.batch.scan;
 
+import org.sonar.api.BatchComponent;
 import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
 
 import org.slf4j.Logger;
@@ -158,7 +159,7 @@ public class ModuleScanContainer extends ComponentContainer {
     ExtensionInstaller installer = getComponentByType(ExtensionInstaller.class);
     installer.install(this, new ExtensionMatcher() {
       public boolean accept(Object extension) {
-        if (ExtensionUtils.isType(extension, BatchExtension.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) {
+        if (ExtensionUtils.isType(extension, BatchComponent.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) {
           // Special use-case: the extension point ProjectBuilder is used in a Maven environment to define some
           // new sub-projects without pom.
           // Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom.
index dee73ecd6c71d8a729adb6741807e3913779809c..e4eacf6aa8d2aebc440b10e1e1432767beaddfca 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.batch.scan;
 
 import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.BatchComponent;
 import org.sonar.api.BatchExtension;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.InstantiationStrategy;
@@ -201,7 +202,7 @@ public class ProjectScanContainer extends ComponentContainer {
 
   static class BatchExtensionFilter implements ExtensionMatcher {
     public boolean accept(Object extension) {
-      return ExtensionUtils.isType(extension, BatchExtension.class)
+      return ExtensionUtils.isType(extension, BatchComponent.class)
         && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH);
     }
   }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java
new file mode 100644 (file)
index 0000000..fc82ece
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.batch.debt;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.technicaldebt.batch.Characteristic;
+import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.utils.Duration;
+
+import java.util.Collections;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DebtDecoratorTest {
+
+  static final int HOURS_IN_DAY = 8;
+
+  static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
+
+  @Mock
+  DecoratorContext context;
+
+  @Mock
+  Resource resource;
+
+  @Mock
+  TechnicalDebtModel debtModel;
+
+  @Mock
+  Issuable issuable;
+
+  @Mock
+  ResourcePerspectives perspectives;
+
+  @Mock
+  RuleFinder ruleFinder;
+
+  RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
+  RuleKey ruleKey2 = RuleKey.of("repo2", "rule2");
+  Rules rules;
+
+  DefaultCharacteristic efficiency = new DefaultCharacteristic().setKey("EFFICIENCY");
+  DefaultCharacteristic memoryEfficiency = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(efficiency);
+
+  DefaultCharacteristic reusability = new DefaultCharacteristic().setKey("REUSABILITY");
+  DefaultCharacteristic modularity = new DefaultCharacteristic().setKey("MODULARITY").setParent(reusability);
+
+  DebtDecorator decorator;
+
+  @Before
+  public void before() throws Exception {
+    when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
+    RulesBuilder rulesBuilder = new RulesBuilder();
+    rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY");
+    rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY");
+    rules = rulesBuilder.build();
+
+    when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()));
+    when(ruleFinder.findByKey(ruleKey2)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()));
+
+    when(debtModel.characteristics()).thenReturn(newArrayList(efficiency, memoryEfficiency, reusability, modularity));
+    when(debtModel.characteristicByKey("EFFICIENCY")).thenReturn(efficiency);
+    when(debtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(memoryEfficiency);
+    when(debtModel.characteristicByKey("REUSABILITY")).thenReturn(reusability);
+    when(debtModel.characteristicByKey("MODULARITY")).thenReturn(modularity);
+
+    decorator = new DebtDecorator(perspectives, debtModel, rules, ruleFinder);
+  }
+
+  @Test
+  public void generates_metrics() throws Exception {
+    assertThat(decorator.generatesMetrics()).hasSize(1);
+  }
+
+  @Test
+  public void execute_on_project() throws Exception {
+    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+  }
+
+  @Test
+  public void not_save_if_measure_already_computed() {
+    when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure());
+
+    decorator.decorate(resource, context);
+
+    verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT)));
+  }
+
+  @Test
+  public void add_technical_debt_from_one_issue_and_no_parent() throws Exception {
+    Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
+  }
+
+  @Test
+  public void add_technical_debt_from_one_issue_without_debt() throws Exception {
+    Issue issue = createIssue("rule1", "repo1").setDebt(null);
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0);
+  }
+
+  @Test
+  public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception {
+    Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, ONE_DAY_IN_MINUTES.doubleValue())));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, ONE_DAY_IN_MINUTES.doubleValue())));
+  }
+
+  @Test
+  public void add_technical_debt_from_issues() throws Exception {
+    Long technicalDebt1 = ONE_DAY_IN_MINUTES;
+    Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES;
+
+    Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
+    Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
+    Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
+    Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
+    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
+
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES);
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void add_technical_debt_from_current_and_children_measures() throws Exception {
+    Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+    Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
+
+    when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
+      new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
+        org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null)
+        .setValue(5d * ONE_DAY_IN_MINUTES)
+    ));
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES);
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 7d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 7d * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void add_technical_debt_only_from_children_measures() throws Exception {
+    when(issuable.issues()).thenReturn(Collections.<Issue>emptyList());
+
+    when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
+      new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
+        org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule())
+        , null, null).setValue(5d * ONE_DAY_IN_MINUTES),
+
+      new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
+        org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())
+        , null, null).setValue(10d * ONE_DAY_IN_MINUTES)
+    ));
+    decorator.decorate(resource, context);
+
+    verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES);
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 5d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 10d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 5d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 5d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, reusability, 10d * ONE_DAY_IN_MINUTES)));
+    verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, modularity, 10d * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void always_save_technical_debt_for_positive_values() throws Exception {
+    // for a project
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getResource()).thenReturn(new Project("foo"));
+    decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
+    verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
+
+    // or for a file
+    context = mock(DecoratorContext.class);
+    when(context.getResource()).thenReturn(new File("foo"));
+    decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
+    verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
+  }
+
+  @Test
+  public void always_save_technical_debt_for_project_if_top_characteristic() throws Exception {
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getResource()).thenReturn(new Project("foo"));
+
+    // this is a top characteristic
+    DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("root");
+
+    decorator.saveCharacteristicMeasure(context, rootCharacteristic, 0.0, true);
+    verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT).setCharacteristic(rootCharacteristic));
+  }
+
+  /**
+   * SQALE-147
+   */
+  @Test
+  public void never_save_technical_debt_for_project_if_not_top_characteristic() throws Exception {
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getResource()).thenReturn(new Project("foo"));
+
+    DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY");
+    DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic);
+
+    decorator.saveCharacteristicMeasure(context, characteristic, 0.0, true);
+    verify(context, never()).saveMeasure(any(Measure.class));
+  }
+
+  @Test
+  public void not_save_technical_debt_for_file_if_zero() throws Exception {
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getResource()).thenReturn(new File("foo"));
+
+    decorator.saveCharacteristicMeasure(context, null, 0.0, true);
+    verify(context, never()).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
+  }
+
+  private DefaultIssue createIssue(String ruleKey, String repositoryKey) {
+    return new DefaultIssue().setRuleKey(RuleKey.of(repositoryKey, ruleKey));
+  }
+
+  class IsCharacteristicMeasure extends ArgumentMatcher<Measure> {
+    Metric metric = null;
+    Characteristic characteristic = null;
+    Double value = null;
+
+    public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) {
+      this.metric = metric;
+      this.characteristic = characteristic;
+      this.value = value;
+    }
+
+    @Override
+    public boolean matches(Object o) {
+      if (!(o instanceof Measure)) {
+        return false;
+      }
+      Measure m = (Measure) o;
+      return ObjectUtils.equals(metric, m.getMetric()) &&
+        ObjectUtils.equals(characteristic, m.getCharacteristic()) &&
+        ObjectUtils.equals(value, m.getValue());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+      description.appendText(new StringBuilder()
+        .append("value=").append(value).append(",")
+        .append("characteristic=").append(characteristic.key()).append(",")
+        .append("metric=").append(metric.getKey()).toString())
+      ;
+    }
+  }
+
+  class IsRuleMeasure extends ArgumentMatcher<RuleMeasure> {
+    Metric metric = null;
+    RuleKey ruleKey = null;
+    Double value = null;
+
+    public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) {
+      this.metric = metric;
+      this.ruleKey = ruleKey;
+      this.value = value;
+    }
+
+    @Override
+    public boolean matches(Object o) {
+      if (!(o instanceof RuleMeasure)) {
+        return false;
+      }
+      RuleMeasure m = (RuleMeasure) o;
+      return ObjectUtils.equals(metric, m.getMetric()) &&
+        ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) &&
+        ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) &&
+        ObjectUtils.equals(value, m.getValue());
+    }
+
+    @Override
+    public void describeTo(Description description) {
+      description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE));
+    }
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/NewDebtDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/NewDebtDecoratorTest.java
new file mode 100644 (file)
index 0000000..a80a19f
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.batch.debt;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.config.Settings;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.utils.Duration;
+import org.sonar.batch.components.Period;
+import org.sonar.batch.components.TimeMachineConfiguration;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.debt.NewDebtDecorator;
+
+import java.util.Date;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class NewDebtDecoratorTest {
+
+  NewDebtDecorator decorator;
+
+  @Mock
+  TimeMachineConfiguration timeMachineConfiguration;
+
+  @Mock
+  Resource resource;
+
+  @Mock
+  Issuable issuable;
+
+  @Mock
+  DecoratorContext context;
+
+  Date rightNow;
+  Date elevenDaysAgo;
+  Date tenDaysAgo;
+  Date nineDaysAgo;
+  Date fiveDaysAgo;
+  Date fourDaysAgo;
+
+  static final int HOURS_IN_DAY = 8;
+
+  static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
+  static final Long TWO_DAYS_IN_MINUTES = 2L * HOURS_IN_DAY * 60;
+  static final Long FIVE_DAYS_IN_MINUTES = 5L * HOURS_IN_DAY * 60;
+
+  @Before
+  public void setup() {
+    Settings settings = new Settings();
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
+
+    ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
+    when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
+
+    rightNow = new Date();
+    elevenDaysAgo = DateUtils.addDays(rightNow, -11);
+    tenDaysAgo = DateUtils.addDays(rightNow, -10);
+    nineDaysAgo = DateUtils.addDays(rightNow, -9);
+    fiveDaysAgo = DateUtils.addDays(rightNow, -5);
+    fourDaysAgo = DateUtils.addDays(rightNow, -4);
+
+    when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo)));
+
+    decorator = new NewDebtDecorator(perspectives, timeMachineConfiguration, new IssueChangelogDebtCalculator());
+  }
+
+  @Test
+  public void generates_metrics() throws Exception {
+    assertThat(decorator.generatesMetrics()).hasSize(1);
+  }
+
+  @Test
+  public void execute_on_project() throws Exception {
+    assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+  }
+
+  @Test
+  public void save_on_one_issue_with_one_new_changelog() {
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        // changelog created at is null because it has just been created on the current analysis
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0 * ONE_DAY_IN_MINUTES, 1.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_one_issue_with_changelog() {
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_one_issue_with_changelog_only_in_the_past() {
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
+      newArrayList(
+        // Change before all periods
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
+  }
+
+  @Test
+  public void save_on_one_issue_with_changelog_having_null_value() {
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
+    when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
+
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() {
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs()
+          .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo)
+          .setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_issues_with_changelog() {
+    Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+      )
+    );
+    Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 7.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_one_issue_without_changelog() {
+    when(issuable.issues()).thenReturn(newArrayList(
+      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
+    );
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_one_issue_without_technical_debt_and_without_changelog() {
+    when(issuable.issues()).thenReturn(newArrayList(
+      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(null))
+    );
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
+  }
+
+  @Test
+  public void save_on_one_issue_without_changelog_and_periods_have_no_dates() {
+    when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
+
+    when(issuable.issues()).thenReturn(newArrayList(
+      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
+    );
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is null, period2 is null
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_issues_without_changelog() {
+    when(issuable.issues()).thenReturn(newArrayList(
+      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
+      new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
+    ));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void save_on_issues_with_changelog_and_issues_without_changelog() {
+    // issue1 and issue2 have changelog
+    Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+      )
+    );
+    Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
+      newArrayList(
+        new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
+        new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+      )
+    );
+
+    // issue3 and issue4 have no changelog
+    Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES));
+    Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES));
+    when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 14.0 * ONE_DAY_IN_MINUTES)));
+  }
+
+  @Test
+  public void not_save_if_measure_already_computed() {
+    when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure());
+    when(issuable.issues()).thenReturn(newArrayList(
+      (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
+      new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
+    ));
+
+    decorator.decorate(resource, context);
+
+    verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
+  }
+
+  /**
+   * SONAR-5059
+   */
+  @Test
+  public void not_return_negative_debt() {
+    Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
+      newArrayList(
+        // changelog created at is null because it has just been created on the current analysis
+        new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null)
+      )
+    );
+    when(issuable.issues()).thenReturn(newArrayList(issue));
+
+    decorator.decorate(resource, context);
+
+    // remember : period1 is 5daysAgo, period2 is 10daysAgo
+    verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
+  }
+
+
+  class IsVariationMeasure extends ArgumentMatcher<Measure> {
+    Metric metric = null;
+    Double var1 = null;
+    Double var2 = null;
+
+    public IsVariationMeasure(Metric metric, Double var1, Double var2) {
+      this.metric = metric;
+      this.var1 = var1;
+      this.var2 = var2;
+    }
+
+    public boolean matches(Object o) {
+      if (!(o instanceof Measure)) {
+        return false;
+      }
+      Measure m = (Measure) o;
+      return ObjectUtils.equals(metric, m.getMetric()) &&
+        ObjectUtils.equals(var1, m.getVariation1()) &&
+        ObjectUtils.equals(var2, m.getVariation2());
+    }
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ignore/IssueExclusionsConfigurationTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ignore/IssueExclusionsConfigurationTest.java
deleted file mode 100644 (file)
index 79849ba..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.batch.issue.ignore;
-
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
-import org.junit.Test;
-import static org.fest.assertions.Assertions.assertThat;
-
-public class IssueExclusionsConfigurationTest {
-  @Test
-  public void justForCoverage() {
-    assertThat(IssueExclusionsConfiguration.getPropertyDefinitions()).hasSize(4);
-  }
-}
index f478c284184d008213c5d4b2ecf130dd517a211a..a070786701916e6cfe41bdc520c059b27286e7e0 100644 (file)
 
 package org.sonar.batch.issue.ignore.pattern;
 
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.SonarException;
+import org.sonar.core.config.IssueExclusionProperties;
+
 import static org.fest.assertions.Assertions.assertThat;
 
 public class IssueExclusionPatternInitializerTest {
@@ -38,7 +38,7 @@ public class IssueExclusionPatternInitializerTest {
 
   @Before
   public void init() {
-    settings = new Settings(new PropertyDefinitions(IssueExclusionsConfiguration.getPropertyDefinitions()));
+    settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all()));
     patternsInitializer = new IssueExclusionPatternInitializer(settings);
   }
 
@@ -51,11 +51,11 @@ public class IssueExclusionPatternInitializerTest {
 
   @Test
   public void shouldHavePatternsBasedOnMulticriteriaPattern() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY, "1,2");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Bar.java");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "*");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Hello.java");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RULE_KEY, "checkstyle:MagicNumber");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1,2");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber");
     patternsInitializer.initPatterns();
 
     assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
@@ -77,29 +77,29 @@ public class IssueExclusionPatternInitializerTest {
 
   @Test(expected = SonarException.class)
   public void shouldLogInvalidResourceKey() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY, "1");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "*");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*");
     patternsInitializer.initPatterns();
   }
 
   @Test(expected = SonarException.class)
   public void shouldLogInvalidRuleKey() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY, "1");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "*");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "*");
+    settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "");
     patternsInitializer.initPatterns();
   }
 
   @Test
   public void shouldReturnBlockPattern() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY, "1,2,3");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "// SONAR-OFF");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "// SONAR-ON");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "// FOO-OFF");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "// FOO-ON");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "// IGNORE-TO-EOF");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1,2,3");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// SONAR-OFF");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// FOO-OFF");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.END_BLOCK_REGEXP, "// FOO-ON");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// IGNORE-TO-EOF");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.END_BLOCK_REGEXP, "");
     patternsInitializer.loadFileContentPatterns();
 
     assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
@@ -112,17 +112,17 @@ public class IssueExclusionPatternInitializerTest {
 
   @Test(expected = SonarException.class)
   public void shouldLogInvalidStartBlockPattern() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY, "1");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "// SONAR-ON");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON");
     patternsInitializer.loadFileContentPatterns();
   }
 
   @Test
   public void shouldReturnAllFilePattern() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY, "1,2");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionsConfiguration.FILE_REGEXP, "@SONAR-IGNORE-ALL");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + ".2." + IssueExclusionsConfiguration.FILE_REGEXP, "//FOO-IGNORE-ALL");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1,2");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "@SONAR-IGNORE-ALL");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".2." + IssueExclusionProperties.FILE_REGEXP, "//FOO-IGNORE-ALL");
     patternsInitializer.loadFileContentPatterns();
 
     assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
@@ -135,8 +135,8 @@ public class IssueExclusionPatternInitializerTest {
 
   @Test(expected = SonarException.class)
   public void shouldLogInvalidAllFilePattern() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY, "1");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionsConfiguration.FILE_REGEXP, "");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1");
+    settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "");
     patternsInitializer.loadFileContentPatterns();
   }
 }
index ed93e89c8e78f79417c83a8c1ef28da0bd068147..eae28d70c05d60fcf6cfa1cd68f20276b5c2c787 100644 (file)
 
 package org.sonar.batch.issue.ignore.pattern;
 
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
+import org.sonar.core.config.IssueExclusionProperties;
+
 import static org.fest.assertions.Assertions.assertThat;
 
 public class IssueInclusionPatternInitializerTest {
@@ -37,7 +36,7 @@ public class IssueInclusionPatternInitializerTest {
 
   @Before
   public void init() {
-    settings = new Settings(new PropertyDefinitions(IssueExclusionsConfiguration.getPropertyDefinitions()));
+    settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all()));
     patternsInitializer = new IssueInclusionPatternInitializer(settings);
   }
 
@@ -49,11 +48,11 @@ public class IssueInclusionPatternInitializerTest {
 
   @Test
   public void shouldHavePatternsBasedOnMulticriteriaPattern() {
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY, "1,2");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Bar.java");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "*");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Hello.java");
-    settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RULE_KEY, "checkstyle:MagicNumber");
+    settings.setProperty("sonar.issue.enforce" + ".multicriteria", "1,2");
+    settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java");
+    settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "ruleKey", "*");
+    settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java");
+    settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber");
     patternsInitializer.initPatterns();
 
     assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
diff --git a/sonar-batch/src/test/java/org/sonar/batch/maven/DefaultMavenPluginExecutorTest.java b/sonar-batch/src/test/java/org/sonar/batch/maven/DefaultMavenPluginExecutorTest.java
new file mode 100644 (file)
index 0000000..75897a2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.maven.project.MavenProject;
+import org.junit.Test;
+import org.sonar.api.batch.maven.MavenPlugin;
+import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+
+import java.io.File;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+public class DefaultMavenPluginExecutorTest {
+
+  @Test
+  public void plugin_version_should_be_optional() {
+    assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", null, "goal"), is("group:artifact::goal"));
+  }
+
+  @Test
+  public void test_plugin_version() {
+    assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", "3.54", "goal"), is("group:artifact:3.54:goal"));
+  }
+
+  @Test
+  public void should_ignore_non_maven_projects() {
+    DefaultMavenPluginExecutor executor = new DefaultMavenPluginExecutor(null, null, mock(MavenProjectConverter.class)) {
+      @Override
+      public void concreteExecute(MavenProject pom, String goal) {
+        pom.addCompileSourceRoot("src/java");
+      }
+    };
+    Project foo = new Project("foo");
+    DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
+    executor.execute(foo, fs, new AddSourceMavenPluginHandler());
+
+    verify(fs, never()).resetDirs(any(File.class), any(File.class), anyList(), anyList(), anyList());
+  }
+
+  static class AddSourceMavenPluginHandler implements MavenPluginHandler {
+    public String getGroupId() {
+      return "fake";
+    }
+
+    public String getArtifactId() {
+      return "fake";
+    }
+
+    public String getVersion() {
+      return "2.2";
+    }
+
+    public boolean isFixedVersion() {
+      return false;
+    }
+
+    public String[] getGoals() {
+      return new String[]{"fake"};
+    }
+
+    public void configure(Project project, MavenPlugin plugin) {
+    }
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/maven/MavenProjectBootstrapperTest.java b/sonar-batch/src/test/java/org/sonar/batch/maven/MavenProjectBootstrapperTest.java
new file mode 100644 (file)
index 0000000..f4f7ea6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MavenProjectBootstrapperTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void bootstrap() throws Exception {
+    ProjectDefinition def = mock(ProjectDefinition.class);
+    MavenSession session = mock(MavenSession.class);
+    MavenProject rootProject = new MavenProject();
+    rootProject.setExecutionRoot(true);
+    List<MavenProject> projects = Arrays.asList(rootProject);
+    when(session.getSortedProjects()).thenReturn(projects);
+
+    MavenProjectConverter pomConverter = mock(MavenProjectConverter.class);
+    when(pomConverter.configure(projects, rootProject)).thenReturn(def);
+    MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
+
+    ProjectReactor reactor = bootstrapper.bootstrap();
+
+    assertThat(reactor).isNotNull();
+    verify(pomConverter).configure(projects, rootProject);
+  }
+
+  @Test
+  public void should_fail_if_no_top_level_project() throws Exception {
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("Maven session does not declare a top level project");
+
+    MavenSession session = mock(MavenSession.class);
+    MavenProjectConverter pomConverter = new MavenProjectConverter();
+    MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
+
+    bootstrapper.bootstrap();
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/maven/MavenProjectConverterTest.java b/sonar-batch/src/test/java/org/sonar/batch/maven/MavenProjectConverterTest.java
new file mode 100644 (file)
index 0000000..be1fd2c
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.hamcrest.core.Is;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.test.TestUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Properties;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+public class MavenProjectConverterTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  MavenProjectConverter converter = new MavenProjectConverter();
+
+  /**
+   * See SONAR-2681
+   */
+  @Test
+  public void shouldThrowExceptionWhenUnableToDetermineProjectStructure() {
+    MavenProject root = new MavenProject();
+    root.setFile(new File("/foo/pom.xml"));
+    root.getBuild().setDirectory("target");
+    root.getModules().add("module/pom.xml");
+
+    try {
+      converter.configure(Arrays.asList(root), root);
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e.getMessage(), containsString("Advanced Reactor Options"));
+    }
+  }
+
+  @Test
+  public void shouldConvertModules() throws IOException {
+    File basedir = temp.newFolder();
+
+    MavenProject root = newMavenProject("com.foo", "parent", "1.0-SNAPSHOT");
+    root.setFile(new File(basedir, "pom.xml"));
+    root.getBuild().setDirectory("target");
+    root.getBuild().setOutputDirectory("target/classes");
+    root.getModules().add("module/pom.xml");
+    MavenProject module = newMavenProject("com.foo", "moduleA", "1.0-SNAPSHOT");
+    module.setFile(new File(basedir, "module/pom.xml"));
+    module.getBuild().setDirectory("target");
+    module.getBuild().setOutputDirectory("target/classes");
+    ProjectDefinition project = converter.configure(Arrays.asList(root, module), root);
+
+    assertThat(project.getSubProjects().size(), is(1));
+  }
+
+  private MavenProject newMavenProject(String groupId, String artifactId, String version) {
+    Model model = new Model();
+    model.setGroupId(groupId);
+    model.setArtifactId(artifactId);
+    model.setVersion(version);
+    return new MavenProject(model);
+  }
+
+  @Test
+  public void shouldConvertProperties() {
+    MavenProject pom = new MavenProject();
+    pom.setGroupId("foo");
+    pom.setArtifactId("bar");
+    pom.setVersion("1.0.1");
+    pom.setName("Test");
+    pom.setDescription("just test");
+    pom.setFile(new File("/foo/pom.xml"));
+    pom.getBuild().setDirectory("target");
+    ProjectDefinition project = ProjectDefinition.create();
+    converter.merge(pom, project);
+
+    Properties properties = project.getProperties();
+    assertThat(properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY), is("foo:bar"));
+    assertThat(properties.getProperty(CoreProperties.PROJECT_VERSION_PROPERTY), is("1.0.1"));
+    assertThat(properties.getProperty(CoreProperties.PROJECT_NAME_PROPERTY), is("Test"));
+    assertThat(properties.getProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY), is("just test"));
+  }
+
+  @Test
+  public void moduleNameShouldEqualArtifactId() throws Exception {
+    File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/");
+    MavenProject parent = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml", true);
+    MavenProject module1 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml", false);
+    MavenProject module2 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml", false);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
+
+    assertThat(rootDef.getSubProjects().size(), Is.is(2));
+    assertThat(rootDef.getKey(), Is.is("org.test:parent"));
+    assertNull(rootDef.getParent());
+    assertThat(rootDef.getBaseDir(), is(rootDir));
+
+    ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
+    assertThat(module1Def.getKey(), Is.is("org.test:module1"));
+    assertThat(module1Def.getParent(), Is.is(rootDef));
+    assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module1")));
+    assertThat(module1Def.getSubProjects().size(), Is.is(0));
+  }
+
+  @Test
+  public void moduleNameDifferentThanArtifactId() throws Exception {
+    File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/");
+    MavenProject parent = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml", true);
+    MavenProject module1 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml", false);
+    MavenProject module2 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml", false);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
+
+    assertThat(rootDef.getSubProjects().size(), is(2));
+    assertThat(rootDef.getKey(), is("org.test:parent"));
+    assertNull(rootDef.getParent());
+    assertThat(rootDef.getBaseDir(), is(rootDir));
+
+    ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
+    assertThat(module1Def.getKey(), Is.is("org.test:module1"));
+    assertThat(module1Def.getParent(), Is.is(rootDef));
+    assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "path1")));
+    assertThat(module1Def.getSubProjects().size(), Is.is(0));
+  }
+
+  @Test
+  public void should_find_module_with_maven_project_file_naming_different_from_pom_xml() throws Exception {
+    File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/");
+    MavenProject parent = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml", true);
+    MavenProject module = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml", false);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module), parent);
+
+    assertThat(rootDef.getSubProjects().size(), Is.is(1));
+    assertThat(rootDef.getKey(), Is.is("org.test:parent"));
+    assertNull(rootDef.getParent());
+    assertThat(rootDef.getBaseDir(), is(rootDir));
+
+    ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
+    assertThat(module1Def.getKey(), Is.is("org.test:module"));
+    assertThat(module1Def.getParent(), Is.is(rootDef));
+    assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module")));
+    assertThat(module1Def.getSubProjects().size(), Is.is(0));
+  }
+
+  @Test
+  public void testSingleProjectWithoutModules() throws Exception {
+    File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/");
+    MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml", true);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+    assertThat(rootDef.getKey(), is("org.test:parent"));
+    assertThat(rootDef.getSubProjects().size(), is(0));
+    assertNull(rootDef.getParent());
+    assertThat(rootDef.getBaseDir(), is(rootDir));
+  }
+
+  @Test
+  public void shouldConvertLinksToProperties() throws Exception {
+    MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinks/pom.xml", true);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+    Properties props = rootDef.getProperties();
+    assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com");
+    assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
+    assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
+    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com");
+    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
+  }
+
+  @Test
+  public void shouldNotConvertLinksToPropertiesIfPropertyAlreadyDefined() throws Exception {
+    MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml", true);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+    Properties props = rootDef.getProperties();
+
+    // Those properties have been fed by the POM elements <ciManagement>, <issueManagement>, ...
+    assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
+    assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
+    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
+
+    // ... but those ones have been overridden by <properties> in the POM
+    assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com-OVERRIDEN-BY-PROPS");
+    assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com-OVERRIDEN-BY-PROPS");
+  }
+
+  @Test
+  public void shouldLoadSourceEncoding() throws Exception {
+    MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/sourceEncoding/pom.xml", true);
+
+    ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+    assertThat(rootDef.getProperties().getProperty(CoreProperties.ENCODING_PROPERTY)).isEqualTo("Shift_JIS");
+  }
+
+  private MavenProject loadPom(String pomPath, boolean isRoot) throws URISyntaxException, IOException, XmlPullParserException {
+    File pomFile = new File(getClass().getResource(pomPath).toURI());
+    Model model = new MavenXpp3Reader().read(new StringReader(FileUtils.readFileToString(pomFile)));
+    MavenProject pom = new MavenProject(model);
+    pom.setFile(pomFile);
+    pom.getBuild().setDirectory("target");
+    pom.setExecutionRoot(isRoot);
+    return pom;
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/maven/SonarMavenProjectBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/maven/SonarMavenProjectBuilderTest.java
new file mode 100644 (file)
index 0000000..16cbd06
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SonarMavenProjectBuilderTest {
+
+  @Test
+  public void testSimpleProject() {
+    MavenSession session = mock(MavenSession.class);
+    MavenProject rootProject = mock(MavenProject.class);
+    when(rootProject.isExecutionRoot()).thenReturn(true);
+    when(session.getSortedProjects()).thenReturn(Arrays.asList(rootProject));
+
+    MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
+    ProjectDefinition projectDefinition = ProjectDefinition.create();
+    when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
+    MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
+
+    assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
+
+    ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
+    verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
+    assertThat(argument.getValue()).contains(rootProject);
+  }
+
+  @Test
+  public void testMultimoduleProject() {
+    MavenSession session = mock(MavenSession.class);
+    MavenProject rootProject = mock(MavenProject.class);
+    MavenProject module1 = mock(MavenProject.class);
+    MavenProject module2 = mock(MavenProject.class);
+    when(rootProject.isExecutionRoot()).thenReturn(true);
+    when(module1.isExecutionRoot()).thenReturn(false);
+    when(module2.isExecutionRoot()).thenReturn(false);
+    when(session.getSortedProjects()).thenReturn(Arrays.asList(module1, module2, rootProject));
+
+    MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
+    ProjectDefinition projectDefinition = ProjectDefinition.create();
+    when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
+    MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
+
+    assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
+
+    ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
+    verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
+    assertThat(argument.getValue()).contains(module1, module2, rootProject);
+  }
+
+}
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom.xml
new file mode 100644 (file)
index 0000000..7b49562
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>module</artifactId>
+  <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml
new file mode 100644 (file)
index 0000000..7b49562
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>module</artifactId>
+  <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml
new file mode 100644 (file)
index 0000000..9d2244e
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <modules>
+    <module>module</module>
+  </modules>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml
new file mode 100644 (file)
index 0000000..470f2d1
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>module1</artifactId>
+  <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml
new file mode 100644 (file)
index 0000000..8810167
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>module2</artifactId>
+  <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml
new file mode 100644 (file)
index 0000000..afd92c0
--- /dev/null
@@ -0,0 +1,12 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <modules>
+    <module>path1</module>
+    <module>path2</module>
+  </modules>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml
new file mode 100644 (file)
index 0000000..470f2d1
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>module1</artifactId>
+  <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml
new file mode 100644 (file)
index 0000000..8810167
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.test</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>module2</artifactId>
+  <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml
new file mode 100644 (file)
index 0000000..cc73a43
--- /dev/null
@@ -0,0 +1,12 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <modules>
+    <module>module1</module>
+    <module>module2</module>
+  </modules>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinks/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinks/pom.xml
new file mode 100644 (file)
index 0000000..460e896
--- /dev/null
@@ -0,0 +1,18 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <url>http://home.com</url>
+  <ciManagement>
+    <url>http://ci.com</url>
+  </ciManagement>
+  <issueManagement>
+    <url>http://issues.com</url>
+  </issueManagement>
+  <scm>
+    <url>http://sources.com</url>
+    <developerConnection>http://sources-dev.com</developerConnection>
+  </scm>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml
new file mode 100644 (file)
index 0000000..5b024e5
--- /dev/null
@@ -0,0 +1,27 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <url>http://home.com</url>
+  <ciManagement>
+    <url>http://ci.com</url>
+  </ciManagement>
+  <issueManagement>
+    <url>http://issues.com</url>
+  </issueManagement>
+  <scm>
+    <url>http://sources.com</url>
+    <developerConnection>http://sources-dev.com</developerConnection>
+  </scm>
+
+
+
+  <properties>
+    <sonar.links.homepage>http://home.com-OVERRIDEN-BY-PROPS</sonar.links.homepage>
+    <sonar.links.scm>http://sources.com-OVERRIDEN-BY-PROPS</sonar.links.scm>
+  </properties>
+
+
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml
new file mode 100644 (file)
index 0000000..ffd4053
--- /dev/null
@@ -0,0 +1,8 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+</project>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/sourceEncoding/pom.xml b/sonar-batch/src/test/resources/org/sonar/batch/maven/MavenProjectConverterTest/sourceEncoding/pom.xml
new file mode 100644 (file)
index 0000000..01bf73c
--- /dev/null
@@ -0,0 +1,11 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.test</groupId>
+  <artifactId>parent</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <url>http://home.com</url>
+  <properties>
+    <project.build.sourceEncoding>Shift_JIS</project.build.sourceEncoding>
+  </properties>
+</project>
\ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
new file mode 100644 (file)
index 0000000..82bb38e
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.config;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+public class CorePropertyDefinitions {
+
+  private CorePropertyDefinitions() {
+    // only static stuff
+  }
+
+  public static List<PropertyDefinition> all() {
+    List<PropertyDefinition> defs = Lists.newArrayList();
+    defs.addAll(IssueExclusionProperties.all());
+    defs.addAll(ExclusionProperties.all());
+    defs.addAll(SecurityProperties.all());
+
+    defs.addAll(ImmutableList.of(
+
+      // DEBT
+      PropertyDefinition.builder(CoreProperties.HOURS_IN_DAY)
+        .name("Number of working hours in a day")
+        .type(PropertyType.INTEGER)
+        .defaultValue("8")
+        .category(CoreProperties.CATEGORY_TECHNICAL_DEBT)
+        .deprecatedKey("sqale.hoursInDay")
+        .build(),
+
+      // BATCH
+
+      PropertyDefinition.builder(CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY)
+        .defaultValue("en")
+        .name("Locale used for issue messages")
+        .description("Deprecated property. Keep default value for backward compatibility.")
+        .hidden()
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 1)
+        .name("Period 1")
+        .description("Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
+          "analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
+          "compare to previous analysis</li><li>'previous_version' to compare to the previous version in the project history</li></ul>" +
+          "<p>When specifying a number of days or a date, the snapshot selected for comparison is " +
+          " the first one available inside the corresponding time range. </p>" +
+          "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
+        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1)
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 2)
+        .name("Period 2")
+        .description("See the property 'Period 1'")
+        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2)
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 3)
+        .name("Period 3")
+        .description("See the property 'Period 1'")
+        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3)
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 4)
+        .name("Period 4")
+        .description("Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
+          "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
+          "for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li>" +
+          "<li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example 1.2</li></ul>" +
+          "<p>When specifying a number of days or a date, the snapshot selected for comparison is the first one available inside the corresponding time range. </p>" +
+          "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
+        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4)
+        .onlyOnQualifiers(Qualifiers.PROJECT)
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 5)
+        .name("Period 5")
+        .description("See the property 'Period 4'")
+        .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5)
+        .onlyOnQualifiers(Qualifiers.PROJECT)
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+        .build()
+    ));
+    return defs;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/config/ExclusionProperties.java b/sonar-core/src/main/java/org/sonar/core/config/ExclusionProperties.java
new file mode 100644 (file)
index 0000000..47756b8
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.config;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+public class ExclusionProperties {
+
+  private ExclusionProperties() {
+    // only static stuff
+  }
+
+  public static List<PropertyDefinition> all() {
+    return ImmutableList.of(
+
+      // COVERAGE
+      PropertyDefinition.builder("sonar.coverage.exclusions")
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_COVERAGE_EXCLUSIONS)
+        .type(PropertyType.STRING)
+        .multiValues(true)
+        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+        .build(),
+
+      // FILES
+      PropertyDefinition.builder(CoreProperties.PROJECT_INCLUSIONS_PROPERTY)
+        .name("Source File Inclusions")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(3)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY)
+        .name("Test File Inclusions")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(5)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY)
+        .name("Global Source File Exclusions")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .index(0)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY)
+        .name("Global Test File Exclusions")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .index(1)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY)
+        .name("Source File Exclusions")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(2)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY)
+        .name("Test File Exclusions")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(4)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.CORE_SKIPPED_MODULES_PROPERTY)
+        .name("Exclude Modules")
+        .description("Maven artifact ids of modules to exclude.")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .onlyOnQualifiers(Qualifiers.PROJECT)
+        .index(0)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.CORE_INCLUDED_MODULES_PROPERTY)
+        .name("Include Modules")
+        .description("Maven artifact ids of modules to include.")
+        .multiValues(true)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+        .onlyOnQualifiers(Qualifiers.PROJECT)
+        .index(1)
+        .build()
+    );
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/config/IssueExclusionProperties.java b/sonar-core/src/main/java/org/sonar/core/config/IssueExclusionProperties.java
new file mode 100644 (file)
index 0000000..b98f1ca
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.config;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.config.PropertyFieldDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+public final class IssueExclusionProperties {
+
+  public static final String SUB_CATEGORY_IGNORE_ISSUES = "issues";
+
+  public static final String EXCLUSION_KEY_PREFIX = "sonar.issue.ignore";
+  public static final String INCLUSION_KEY_PREFIX = "sonar.issue.enforce";
+
+  public static final String MULTICRITERIA_SUFFIX = ".multicriteria";
+  public static final String PATTERNS_MULTICRITERIA_EXCLUSION_KEY = EXCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
+  public static final String PATTERNS_MULTICRITERIA_INCLUSION_KEY = INCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
+  public static final String RESOURCE_KEY = "resourceKey";
+  private static final String PROPERTY_FILE_PATH_PATTERN = "File Path Pattern";
+  public static final String RULE_KEY = "ruleKey";
+  private static final String PROPERTY_RULE_KEY_PATTERN = "Rule Key Pattern";
+  private static final String PROPERTY_RULE_KEY_PATTERN_HELP = "<br/>A rule key pattern consists of the rule repository name, followed by a colon, followed by a rule key "
+    + "or rule name fragment. For example:"
+    + "<ul><li>squid:S1195</li><li>squid:*Naming*</li></ul>";
+
+  public static final String BLOCK_SUFFIX = ".block";
+  public static final String PATTERNS_BLOCK_KEY = EXCLUSION_KEY_PREFIX + BLOCK_SUFFIX;
+  public static final String BEGIN_BLOCK_REGEXP = "beginBlockRegexp";
+  public static final String END_BLOCK_REGEXP = "endBlockRegexp";
+
+  public static final String ALLFILE_SUFFIX = ".allfile";
+  public static final String PATTERNS_ALLFILE_KEY = EXCLUSION_KEY_PREFIX + ALLFILE_SUFFIX;
+  public static final String FILE_REGEXP = "fileRegexp";
+
+  public static final int LARGE_SIZE = 40;
+
+  private IssueExclusionProperties() {
+    // only static
+  }
+
+  public static List<PropertyDefinition> all() {
+    return ImmutableList.of(
+      PropertyDefinition.builder(PATTERNS_MULTICRITERIA_EXCLUSION_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+        .name("Ignore Issues on Multiple Criteria")
+        .description("Patterns to ignore issues on certain components and for certain coding rules." + PROPERTY_RULE_KEY_PATTERN_HELP)
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(3)
+        .fields(
+          PropertyFieldDefinition.build(RULE_KEY)
+            .name(PROPERTY_RULE_KEY_PATTERN)
+            .description("Pattern to match rules which should be ignored.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build(),
+          PropertyFieldDefinition.build(RESOURCE_KEY)
+            .name(PROPERTY_FILE_PATH_PATTERN)
+            .description("Pattern to match files which should be ignored.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build()
+        )
+        .build(),
+      PropertyDefinition.builder(PATTERNS_BLOCK_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+        .name("Ignore Issues in Blocks")
+        .description("Patterns to ignore all issues on specific blocks of code, while continuing to scan and mark issues on the remainder of the file.")
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(2)
+        .fields(
+          PropertyFieldDefinition.build(BEGIN_BLOCK_REGEXP)
+            .name("Regular Expression for Start of Block")
+            .description("If this regular expression is found in a file, then following lines are ignored until end of block.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build(),
+          PropertyFieldDefinition.build(END_BLOCK_REGEXP)
+            .name("Regular Expression for End of Block")
+            .description("If specified, this regular expression is used to determine the end of code blocks to ignore. If not, then block ends at the end of file.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build()
+        )
+        .build(),
+      PropertyDefinition.builder(PATTERNS_ALLFILE_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+        .name("Ignore Issues on Files")
+        .description("Patterns to ignore all issues on files that contain a block of code matching a given regular expression.")
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(1)
+        .fields(
+          PropertyFieldDefinition.build(FILE_REGEXP)
+            .name("Regular Expression")
+            .description("If this regular expression is found in a file, then the whole file is ignored.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build()
+        )
+        .build(),
+      PropertyDefinition.builder(PATTERNS_MULTICRITERIA_INCLUSION_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+        .name("Restrict Scope of Coding Rules")
+        .description("Patterns to restrict the application of a rule to only certain components, ignoring all others." + PROPERTY_RULE_KEY_PATTERN_HELP)
+        .onQualifiers(Qualifiers.PROJECT)
+        .index(4)
+        .fields(
+          PropertyFieldDefinition.build(RULE_KEY)
+            .name(PROPERTY_RULE_KEY_PATTERN)
+            .description("Pattern used to match rules which should be restricted.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build(),
+          PropertyFieldDefinition.build(RESOURCE_KEY)
+            .name(PROPERTY_FILE_PATH_PATTERN)
+            .description("Pattern used to match files to which the rules should be restricted.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build()
+        )
+        .build()
+    );
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/config/SecurityProperties.java b/sonar-core/src/main/java/org/sonar/core/config/SecurityProperties.java
new file mode 100644 (file)
index 0000000..3915744
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.config;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+class SecurityProperties {
+
+  private SecurityProperties() {
+    // only static stuff
+  }
+
+  static List<PropertyDefinition> all() {
+    return ImmutableList.of(
+      PropertyDefinition.builder(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY)
+        .defaultValue("" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE)
+        .name("Import sources")
+        .description("Set to false if sources should not be imported and therefore not available in the Web UI (e.g. for security reasons).")
+        .type(PropertyType.BOOLEAN)
+        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+        .category(CoreProperties.CATEGORY_SECURITY)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY)
+        .defaultValue("" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE)
+        .name("Allow users to sign up online")
+        .description("Users can sign up online.")
+        .type(PropertyType.BOOLEAN)
+        .category(CoreProperties.CATEGORY_SECURITY)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.CORE_DEFAULT_GROUP)
+        .defaultValue(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE)
+        .name("Default user group")
+        .description("Any new users will automatically join this group.")
+        .category(CoreProperties.CATEGORY_SECURITY)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY)
+        .defaultValue("" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE)
+        .name("Force user authentication")
+        .description("Forcing user authentication stops un-logged users to access SonarQube.")
+        .type(PropertyType.BOOLEAN)
+        .category(CoreProperties.CATEGORY_SECURITY)
+        .build(),
+
+      PropertyDefinition.builder(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION)
+        .defaultValue(Boolean.toString(false))
+        .name("Prevent automatic project creation")
+        .description("Set to true to prevent automatic project creation at first analysis and force project provisioning.")
+        .type(PropertyType.BOOLEAN)
+        .category(CoreProperties.CATEGORY_SECURITY)
+        .build()
+    );
+
+  }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java b/sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java
new file mode 100644 (file)
index 0000000..5f3b028
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.config;
+
+import org.junit.Test;
+import org.sonar.api.config.PropertyDefinition;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class CorePropertyDefinitionsTest {
+  @Test
+  public void all() {
+    List<PropertyDefinition> defs = CorePropertyDefinitions.all();
+    assertThat(defs.size()).isGreaterThan(10);
+  }
+}
index 5dd76005a69047b7e138ff19817a91b3abb78b0a..61d381de0aefc9f464bd70fa537c2c1b165b168f 100644 (file)
       <artifactId>gson</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.codehaus.sonar</groupId>
-      <artifactId>sonar-batch</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.codehaus.sonar</groupId>
-      <artifactId>sonar-batch-maven-compat</artifactId>
-      <version>${project.version}</version>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
     </dependency>
     <dependency>
       <groupId>org.codehaus.sonar</groupId>
     <dependency>
       <groupId>org.codehaus.sonar</groupId>
       <artifactId>sonar-deprecated</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-project</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.codehaus.sonar</groupId>
           <type>sonar-plugin</type>
           <scope>provided</scope>
         </dependency>
-        <dependency>
-          <groupId>org.codehaus.sonar.plugins</groupId>
-          <artifactId>sonar-maven-batch-plugin</artifactId>
-          <version>${project.version}</version>
-          <type>sonar-plugin</type>
-          <scope>provided</scope>
-        </dependency>
         <dependency>
           <groupId>org.codehaus.sonar-plugins.java</groupId>
           <artifactId>sonar-jacoco-plugin</artifactId>
           </plugin>
         </plugins>
       </build>
-      <dependencies>
-        <dependency>
-          <groupId>com.h2database</groupId>
-          <artifactId>h2</artifactId>
-        </dependency>
-      </dependencies>
     </profile>
 
     <profile>
index 9932eda1aad636d8e51f898f972b7ff836c3d6d7..8095d1911b6b74898cbc14d491fe76c97064cc77 100644 (file)
@@ -182,10 +182,6 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
     return new File(getDeployDir(), "plugins/index.txt");
   }
 
-  public File getBootstrapIndex() {
-    return new File(getDeployDir(), "bootstrap/index.txt");
-  }
-
   /**
    * @deprecated since 4.1
    */
index 73e14c715303e365581bca9f958b7ef49efbb2e8..e7f9d4d9385ec94c684b0545f6f7a66461e75821 100644 (file)
@@ -39,6 +39,7 @@ import org.sonar.api.utils.UriReader;
 import org.sonar.api.utils.internal.TempFolderCleaner;
 import org.sonar.core.component.SnapshotPerspectives;
 import org.sonar.core.component.db.ComponentDao;
+import org.sonar.core.config.CorePropertyDefinitions;
 import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.i18n.GwtI18n;
@@ -162,6 +163,7 @@ class ServerComponents {
       new TempFolderProvider(),
       System2.INSTANCE
     ));
+    components.addAll(CorePropertyDefinitions.all());
     components.addAll(DatabaseMigrations.CLASSES);
     components.addAll(DaoUtils.getDaoClasses());
     return components;
@@ -191,7 +193,8 @@ class ServerComponents {
 
       // ws
       RestartHandler.class,
-      SystemWs.class
+      SystemWs.class,
+      BatchWs.class
     );
   }
 
@@ -405,7 +408,6 @@ class ServerComponents {
     startupContainer.addSingleton(JdbcDriverDeployer.class);
     startupContainer.addSingleton(RegisterDebtModel.class);
     startupContainer.addSingleton(GeneratePluginIndex.class);
-    startupContainer.addSingleton(GenerateBootstrapIndex.class);
     startupContainer.addSingleton(RegisterNewMeasureFilters.class);
     startupContainer.addSingleton(RegisterDashboards.class);
     startupContainer.addSingleton(RegisterPermissionTemplates.class);
diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java b/sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java
deleted file mode 100644 (file)
index f8daf5a..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.plugins;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * This servlet allows to load libraries from directory "WEB-INF/lib" in order to provide them for batch-bootstrapper.
- * Most probably this is not a best solution.
- */
-public class BatchResourcesServlet extends HttpServlet {
-
-  private static final Logger LOG = LoggerFactory.getLogger(BatchResourcesServlet.class);
-  private static final long serialVersionUID = -2100128371794649028L;
-
-  @Override
-  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    String filename = filename(request);
-    if (StringUtils.isBlank(filename)) {
-      throw new IllegalArgumentException("Filename is missing.");
-    } else {
-      InputStream in = null;
-      OutputStream out = null;
-      try {
-        in = getServletContext().getResourceAsStream("/WEB-INF/lib/" + filename);
-        if (in == null) {
-          // TODO
-        } else {
-          response.setContentType("application/java-archive");
-          out = response.getOutputStream();
-          IOUtils.copy(in, out);
-        }
-      } catch (Exception e) {
-        LOG.error("Unable to load batch resource '" + filename + "'", e);
-        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-      } finally {
-        IOUtils.closeQuietly(in);
-        IOUtils.closeQuietly(out);
-      }
-    }
-  }
-
-  /**
-   * @return part of request URL after servlet path
-   */
-  String filename(HttpServletRequest request) {
-    String filename = null;
-    if (StringUtils.endsWithIgnoreCase(request.getRequestURI(), "jar")) {
-      filename = StringUtils.substringAfterLast(request.getRequestURI(), "/");
-    }
-    return filename;
-  }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/BatchWs.java b/sonar-server/src/main/java/org/sonar/server/plugins/BatchWs.java
new file mode 100644 (file)
index 0000000..25c4cc9
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.plugins;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.HiddenFileFilter;
+import org.apache.commons.lang.CharUtils;
+import org.apache.commons.lang.StringUtils;
+import org.picocontainer.Startable;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.home.cache.FileHashes;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * JAR files to be downloaded by sonar-runner.
+ */
+public class BatchWs implements WebService, Startable {
+
+  private final Server server;
+  private String index;
+  private File batchDir;
+
+  public BatchWs(Server server) {
+    this.server = server;
+  }
+
+  @Override
+  public void start() {
+    StringBuilder sb = new StringBuilder();
+    batchDir = new File(server.getRootDir(), "lib/batch");
+    if (batchDir.exists()) {
+      Collection<File> files = FileUtils.listFiles(batchDir, HiddenFileFilter.VISIBLE, FileFilterUtils.directoryFileFilter());
+      for (File file : files) {
+        String filename = file.getName();
+        if (StringUtils.endsWith(filename, ".jar")) {
+          sb.append(filename).append('|').append(new FileHashes().of(file)).append(CharUtils.LF);
+        }
+      }
+    }
+    this.index = sb.toString();
+  }
+
+  @Override
+  public void stop() {
+    // nothing to do
+  }
+
+  @Override
+  public void define(Context context) {
+    NewController controller = context.createController("batch");
+    controller.createAction("index")
+      .setInternal(true)
+      .setDescription("List the JAR files to be downloaded by source analyzer")
+      .setHandler(new RequestHandler() {
+        @Override
+        public void handle(Request request, Response response) {
+          index(response);
+        }
+      });
+    controller.createAction("file")
+      .setInternal(true)
+      .setDescription("Download a JAR file required by source analyzer")
+      .setHandler(new RequestHandler() {
+        @Override
+        public void handle(Request request, Response response) {
+          file(request, response);
+        }
+      }).createParam("name", "Filename");
+
+    controller.done();
+  }
+
+  private void index(Response response) {
+    try {
+      response.stream().setMediaType("text/plain");
+      IOUtils.write(index, response.stream().output());
+    } catch (IOException e) {
+      throw new IllegalStateException("Fail to send batch index", e);
+    }
+  }
+
+  private void file(Request request, Response response) {
+    String filename = request.mandatoryParam("name");
+    try {
+      File input = new File(batchDir, filename);
+      if (!input.exists() || !FileUtils.directoryContains(batchDir, input)) {
+        throw new IllegalArgumentException("Bad filename: " + filename);
+      }
+      response.stream().setMediaType("application/java-archive");
+      FileUtils.copyFile(input, response.stream().output());
+
+    } catch (IOException e) {
+      throw new IllegalStateException("Fail to send batch file " + filename, e);
+    }
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/startup/GenerateBootstrapIndex.java b/sonar-server/src/main/java/org/sonar/server/startup/GenerateBootstrapIndex.java
deleted file mode 100644 (file)
index 4b71498..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.filefilter.FileFilterUtils;
-import org.apache.commons.io.filefilter.HiddenFileFilter;
-import org.apache.commons.lang.CharUtils;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.platform.Server;
-import org.sonar.home.cache.FileHashes;
-import org.sonar.server.platform.DefaultServerFileSystem;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @since 3.5
- */
-public final class GenerateBootstrapIndex {
-
-  // JARs starting with one of these prefixes are excluded from batch
-  private static final String[] IGNORE = {"jtds", "mysql", "postgresql", "jruby", "jfreechart", "eastwood",
-    "elasticsearch", "lucene"};
-
-  private static final String LIB_DIR = "/web/WEB-INF/lib";
-
-  private final Server server;
-  private final DefaultServerFileSystem fileSystem;
-
-  public GenerateBootstrapIndex(DefaultServerFileSystem fileSystem, Server server) {
-    this.server = server;
-    this.fileSystem = fileSystem;
-  }
-
-  public void start() throws IOException {
-    writeIndex(fileSystem.getBootstrapIndex());
-  }
-
-  void writeIndex(File indexFile) throws IOException {
-    FileUtils.forceMkdir(indexFile.getParentFile());
-    FileWriter writer = new FileWriter(indexFile, false);
-    try {
-      File libDir = new File(server.getRootDir(), LIB_DIR);
-      // TODO hack for Medium tests
-      if (libDir.exists()) {
-        for (String path : getLibs(libDir)) {
-          writer.append(path);
-          File is = new File(libDir, path);
-          writer.append("|").append(new FileHashes().of(is));
-          writer.append(CharUtils.LF);
-        }
-        writer.flush();
-      }
-
-    } finally {
-      IOUtils.closeQuietly(writer);
-    }
-  }
-
-  @VisibleForTesting
-  static List<String> getLibs(File libDir) {
-    List<String> libs = Lists.newArrayList();
-
-    Collection<File> files = FileUtils.listFiles(libDir, HiddenFileFilter.VISIBLE, FileFilterUtils.directoryFileFilter());
-    for (File file : files) {
-      String path = file.getPath();
-      if (StringUtils.endsWith(path, ".jar")) {
-        String filename = StringUtils.removeStart(path, libDir.getAbsolutePath() + "/");
-        if (!isIgnored(filename)) {
-          libs.add(filename);
-        }
-      }
-    }
-    return libs;
-  }
-
-  /**
-   * Dirty hack to disable downloading for certain files.
-   */
-  static boolean isIgnored(String filename) {
-    for (String prefix : IGNORE) {
-      if (StringUtils.startsWith(filename, prefix)) {
-        return true;
-      }
-    }
-    return false;
-  }
-}
index f480f7447ca41442b7dbe27c1809202476168c2b..222fbe111dba3fa1c613342caf3e823ff6c907ae 100644 (file)
@@ -35,7 +35,11 @@ import org.sonar.api.test.MutableTestPlan;
 import org.sonar.api.test.MutableTestable;
 import org.sonar.api.test.TestPlan;
 import org.sonar.api.test.Testable;
-import org.sonar.api.web.*;
+import org.sonar.api.web.Footer;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.Page;
+import org.sonar.api.web.RubyRailsWebservice;
+import org.sonar.api.web.Widget;
 import org.sonar.core.component.SnapshotPerspectives;
 import org.sonar.core.measure.MeasureFilterEngine;
 import org.sonar.core.measure.MeasureFilterResult;
@@ -46,8 +50,15 @@ import org.sonar.core.resource.ResourceIndexerDao;
 import org.sonar.core.resource.ResourceKeyUpdaterDao;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.server.db.migrations.DatabaseMigrator;
-import org.sonar.server.platform.*;
-import org.sonar.server.plugins.*;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.platform.ServerIdGenerator;
+import org.sonar.server.platform.ServerSettings;
+import org.sonar.server.platform.SettingsChangeNotifier;
+import org.sonar.server.plugins.InstalledPluginReferentialFactory;
+import org.sonar.server.plugins.PluginDownloader;
+import org.sonar.server.plugins.ServerPluginJarsInstaller;
+import org.sonar.server.plugins.ServerPluginRepository;
+import org.sonar.server.plugins.UpdateCenterMatrixFactory;
 import org.sonar.server.rule.RuleRepositories;
 import org.sonar.server.source.CodeColorizers;
 import org.sonar.server.user.NewUserNotifier;
@@ -57,7 +68,6 @@ import org.sonar.updatecenter.common.Version;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import java.io.IOException;
 import java.net.InetAddress;
 import java.sql.Connection;
 import java.util.Collection;
@@ -369,10 +379,6 @@ public final class JRubyFacade {
     return Platform.getInstance().getContainer();
   }
 
-  public String boostrapIndexPath() throws IOException {
-    return getContainer().getComponentByType(DefaultServerFileSystem.class).getBootstrapIndex().getCanonicalPath();
-  }
-
   // UPDATE PROJECT KEY ------------------------------------------------------------------
   public void updateResourceKey(long projectId, String newKey) {
     get(ResourceKeyUpdaterDao.class).updateKey(projectId, newKey);
index e8e7f802538fed16d110eea99f2e7fc30eaa1d3f..a12fb2fbc524921ce2468db5b105af71eec1cedb 100644 (file)
@@ -90,11 +90,6 @@ class BatchBootstrapController < Api::ApiController
     render :json => JSON(json_properties)
   end
 
-  # GET /batch_bootstrap/index
-  def index
-    render :file => java_facade.boostrapIndexPath(), :layout => false
-  end
-
   private
 
   def render_unauthorized(message, status=403)
index 2a04f24fd559ba2001a544e9aedbc2e0bf91fd9b..72dbd1eb08bfd7a93f6744b27ec044c327de0abf 100644 (file)
@@ -14,6 +14,11 @@ ActionController::Routing::Routes.draw do |map|
   end
 
   map.connect 'api', :controller => 'api/java_ws', :action => 'redirect_to_ws_listing'
+
+  # deprecated, sonar-runner should use batch/index and batch/file?name=xxx
+  map.connect 'batch_bootstrap/index', :controller => 'api/java_ws', :action => 'index', :wspath => 'batch', :wsaction => 'index'
+  map.connect 'batch/:name', :controller => 'api/java_ws', :action => 'index', :wspath => 'batch', :wsaction => 'file', :requirements => { :name => /.*/ }
+
   map.connect 'api/metrics', :controller => 'api/metrics', :action => 'index', :conditions => { :method => :get }
   map.connect 'api/metrics/:id', :controller => 'api/metrics', :action => 'show', :conditions => { :method => :get }
   map.connect 'api/metrics/:id', :controller => 'api/metrics', :action => 'create', :conditions => { :method => :post }
index 1dff5cbff4cc595e72ee8afb49f238795dacb42b..b4896f15c5666835603c75e544f995c70303b3f2 100644 (file)
     <servlet-name>static</servlet-name>
     <servlet-class>org.sonar.server.plugins.StaticResourcesServlet</servlet-class>
   </servlet>
-  <servlet>
-    <servlet-name>batch</servlet-name>
-    <servlet-class>org.sonar.server.plugins.BatchResourcesServlet</servlet-class>
-  </servlet>
 
   <servlet-mapping>
     <servlet-name>chart</servlet-name>
     <servlet-name>static</servlet-name>
     <url-pattern>/static/*</url-pattern>
   </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>batch</servlet-name>
-    <url-pattern>/batch/*</url-pattern>
-  </servlet-mapping>
 
   <session-config>
     <!-- in minutes -->
diff --git a/sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java b/sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java
deleted file mode 100644 (file)
index 0ad4861..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.plugins;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import javax.servlet.http.HttpServletRequest;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class BatchResourcesServletTest {
-  private BatchResourcesServlet servlet;
-  private HttpServletRequest request;
-
-  @Before
-  public void setUp() throws Exception {
-    servlet = new BatchResourcesServlet();
-    request = mock(HttpServletRequest.class);
-  }
-
-  @Test
-  public void test_filename() {
-    when(request.getContextPath()).thenReturn("sonar");
-    when(request.getServletPath()).thenReturn("/batch");
-
-    when(request.getRequestURI()).thenReturn("/sonar/batch/sonar-core-2.6.jar");
-    assertThat(servlet.filename(request)).isEqualTo("sonar-core-2.6.jar");
-
-    when(request.getRequestURI()).thenReturn("/sonar/batch/");
-    assertThat(servlet.filename(request)).isNull();
-
-    when(request.getRequestURI()).thenReturn("/sonar/batch");
-    assertThat(servlet.filename(request)).isNull();
-
-    when(request.getRequestURI()).thenReturn("/sonar/batch.html");
-    assertThat(servlet.filename(request)).isNull();
-
-    when(request.getRequestURI()).thenReturn("/sonar/batch/index.html");
-    assertThat(servlet.filename(request)).isNull();
-  }
-
-}
diff --git a/sonar-server/src/test/java/org/sonar/server/plugins/BatchWsTest.java b/sonar-server/src/test/java/org/sonar/server/plugins/BatchWsTest.java
new file mode 100644 (file)
index 0000000..d20d346
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.plugins;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.CharUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ws.WsTester;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BatchWsTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  Server server = mock(Server.class);
+
+  @Before
+  public void prepare_fs() throws IOException {
+    File rootDir = temp.newFolder();
+    when(server.getRootDir()).thenReturn(rootDir);
+
+    File batchDir = new File(rootDir, "lib/batch");
+    FileUtils.forceMkdir(batchDir);
+    FileUtils.writeStringToFile(new File(batchDir, "sonar-batch.jar"), "foo");
+  }
+
+  @Test
+  public void download_index() throws Exception {
+    BatchWs ws = new BatchWs(server);
+    ws.start();
+    WsTester tester = new WsTester(ws);
+
+    String index = tester.newRequest("index").execute().outputAsString();
+    assertThat(index).isEqualTo("sonar-batch.jar|acbd18db4cc2f85cedef654fccc4a4d8" + CharUtils.LF);
+
+    ws.stop();
+  }
+
+  @Test
+  public void download_file() throws Exception {
+    BatchWs ws = new BatchWs(server);
+    ws.start();
+    WsTester tester = new WsTester(ws);
+
+    String jar = tester.newRequest("file").setParam("name", "sonar-batch.jar").execute().outputAsString();
+    assertThat(jar).isEqualTo("foo");
+  }
+
+  /**
+   * Do not allow to download files located outside the directory lib/batch, for example
+   * /etc/passwd
+   */
+  @Test
+  public void check_location_of_file() throws Exception {
+    thrown.expect(IllegalArgumentException.class);
+    thrown.expectMessage("Bad filename: ../sonar-batch.jar");
+
+    BatchWs ws = new BatchWs(server);
+    ws.start();
+    WsTester tester = new WsTester(ws);
+
+    tester.newRequest("file").setParam("name", "../sonar-batch.jar").execute();
+  }
+
+  @Test
+  public void file_does_not_exist() throws Exception {
+    thrown.expect(IllegalArgumentException.class);
+    thrown.expectMessage("Bad filename: other.jar");
+
+    BatchWs ws = new BatchWs(server);
+    ws.start();
+    WsTester tester = new WsTester(ws);
+
+    tester.newRequest("file").setParam("name", "other.jar").execute();
+  }
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/startup/GenerateBootstrapIndexTest.java b/sonar-server/src/test/java/org/sonar/server/startup/GenerateBootstrapIndexTest.java
deleted file mode 100644 (file)
index ca6f7c7..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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 this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.io.IOException;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class GenerateBootstrapIndexTest {
-
-  @Rule
-  public TemporaryFolder rootDir = new TemporaryFolder();
-
-  @Before
-  public void setUp() throws Exception {
-  }
-
-  @Test
-  public void determine_list_of_resources() throws IOException {
-    new File(rootDir.getRoot(), "/web/WEB-INF/lib").mkdirs();
-    File webInf = new File(rootDir.getRoot(), "/web/WEB-INF");
-    File lib = new File(rootDir.getRoot(), "/web/WEB-INF/lib");
-    new File(webInf, "directory").mkdir();
-    new File(lib, "sonar-core-2.6.jar").createNewFile();
-    new File(lib, "treemap.rbr").createNewFile();
-    new File(lib, "sonar-core-2.6.jar").createNewFile();
-
-    assertThat(GenerateBootstrapIndex.getLibs(lib)).hasSize(1);
-    assertThat(GenerateBootstrapIndex.getLibs(lib).get(0)).isEqualTo("sonar-core-2.6.jar");
-  }
-
-  @Test
-  public void ignore_some_jars() {
-    assertThat(GenerateBootstrapIndex.isIgnored("sonar-batch-2.6-SNAPSHOT.jar")).isFalse();
-    assertThat(GenerateBootstrapIndex.isIgnored("mysql-connector-java-5.1.13.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("postgresql-9.0-801.jdbc3.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("jtds-1.2.4.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("jfreechart-1.0.9.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("eastwood-1.1.0.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("jruby-complete-1.5.6.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("jruby-rack-1.0.5.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("elasticsearch-0.90.6.jar")).isTrue();
-    assertThat(GenerateBootstrapIndex.isIgnored("lucene-core-4.5.1.jar")).isTrue();
-  }
-
-}