From bbfce797275cf13e8c7a0e43aa29406175c89bfc Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Mon, 7 Feb 2011 13:20:26 +0300 Subject: [PATCH] SONAR-2172: New extension point - Initializer * Rename AbstractInitializer to Initializer * Execute Initializers before Sensors --- .../clover/CloverMavenInitializer.java | 9 +-- .../cobertura/CoberturaMavenInitializer.java | 9 +-- .../batch/phases/InitializersExecutor.java | 73 +++++++++++++++++++ .../java/org/sonar/batch/phases/Phases.java | 17 +++-- ...tractInitializer.java => Initializer.java} | 20 +++-- .../main/java/org/sonar/api/batch/Sensor.java | 43 ++++++----- .../batch/maven/DependsUponMavenPlugin.java | 8 +- .../org/sonar/api/batch/InitializerTest.java | 43 +++++++++++ 8 files changed, 174 insertions(+), 48 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java rename sonar-plugin-api/src/main/java/org/sonar/api/batch/{AbstractInitializer.java => Initializer.java} (69%) create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/batch/InitializerTest.java diff --git a/plugins/sonar-clover-plugin/src/main/java/org/sonar/plugins/clover/CloverMavenInitializer.java b/plugins/sonar-clover-plugin/src/main/java/org/sonar/plugins/clover/CloverMavenInitializer.java index 90026cc17fd..28ae571629f 100644 --- a/plugins/sonar-clover-plugin/src/main/java/org/sonar/plugins/clover/CloverMavenInitializer.java +++ b/plugins/sonar-clover-plugin/src/main/java/org/sonar/plugins/clover/CloverMavenInitializer.java @@ -20,10 +20,8 @@ package org.sonar.plugins.clover; import org.apache.commons.configuration.Configuration; -import org.sonar.api.batch.AbstractInitializer; import org.sonar.api.batch.CoverageExtension; -import org.sonar.api.batch.Phase; -import org.sonar.api.batch.Phase.Name; +import org.sonar.api.batch.Initializer; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; @@ -33,8 +31,7 @@ import org.sonar.api.resources.Project; * Provides {@link CloverMavenPluginHandler} and configures correct path to report. * Enabled only in Maven environment. */ -@Phase(name = Name.PRE) -public class CloverMavenInitializer extends AbstractInitializer implements CoverageExtension, DependsUponMavenPlugin { +public class CloverMavenInitializer extends Initializer implements CoverageExtension, DependsUponMavenPlugin { private CloverMavenPluginHandler handler; @@ -56,7 +53,7 @@ public class CloverMavenInitializer extends AbstractInitializer implements Cover } @Override - public void prepare(Project project) { + public void execute(Project project) { Configuration conf = project.getConfiguration(); if (!conf.containsKey(CloverConstants.REPORT_PATH_PROPERTY)) { String report = getReportPathFromPluginConfiguration(project); diff --git a/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/CoberturaMavenInitializer.java b/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/CoberturaMavenInitializer.java index 502cae448ad..6452a08a0cd 100644 --- a/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/CoberturaMavenInitializer.java +++ b/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/CoberturaMavenInitializer.java @@ -21,10 +21,8 @@ package org.sonar.plugins.cobertura; import org.apache.commons.configuration.Configuration; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.AbstractInitializer; import org.sonar.api.batch.CoverageExtension; -import org.sonar.api.batch.Phase; -import org.sonar.api.batch.Phase.Name; +import org.sonar.api.batch.Initializer; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; @@ -35,8 +33,7 @@ import org.sonar.plugins.cobertura.api.CoberturaUtils; * Provides {@link CoberturaMavenPluginHandler} and configures correct path to report. * Enabled only in Maven environment. */ -@Phase(name = Name.PRE) -public class CoberturaMavenInitializer extends AbstractInitializer implements CoverageExtension, DependsUponMavenPlugin { +public class CoberturaMavenInitializer extends Initializer implements CoverageExtension, DependsUponMavenPlugin { private CoberturaMavenPluginHandler handler; @@ -58,7 +55,7 @@ public class CoberturaMavenInitializer extends AbstractInitializer implements Co } @Override - public void prepare(Project project) { + public void execute(Project project) { Configuration conf = project.getConfiguration(); if (conf.containsKey(CoreProperties.COBERTURA_REPORT_PATH_PROPERTY)) { String report = getReportPathFromPluginConfiguration(project); diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java new file mode 100644 index 00000000000..e84d4527ede --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java @@ -0,0 +1,73 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.phases; + +import java.util.Collection; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.Initializer; +import org.sonar.api.batch.maven.DependsUponMavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.TimeProfiler; +import org.sonar.batch.MavenPluginExecutor; + +public class InitializersExecutor { + + private static final Logger logger = LoggerFactory.getLogger(SensorsExecutor.class); + + private MavenPluginExecutor mavenExecutor; + + private Collection initializers; + + public InitializersExecutor(BatchExtensionDictionnary selector, Project project, MavenPluginExecutor mavenExecutor) { + this.initializers = selector.select(Initializer.class, project, true); + this.mavenExecutor = mavenExecutor; + } + + public void execute(Project project) { + if (logger.isDebugEnabled()) { + logger.debug("Initializers : {}", StringUtils.join(initializers, " -> ")); + } + + for (Initializer initializer : initializers) { + executeMavenPlugin(project, initializer); + + TimeProfiler profiler = new TimeProfiler(logger).start("Initializer " + initializer); + initializer.execute(project); + profiler.stop(); + } + } + + private void executeMavenPlugin(Project project, Initializer sensor) { + if (sensor instanceof DependsUponMavenPlugin) { + MavenPluginHandler handler = ((DependsUponMavenPlugin) sensor).getMavenPluginHandler(project); + if (handler != null) { + TimeProfiler profiler = new TimeProfiler(logger).start("Execute maven plugin " + handler.getArtifactId()); + mavenExecutor.execute(project, handler); + profiler.stop(); + } + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java b/sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java index 3ae372ddb16..4146e92ab75 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java @@ -19,27 +19,28 @@ */ package org.sonar.batch.phases; +import java.util.Arrays; +import java.util.Collection; + import org.sonar.api.batch.SensorContext; import org.sonar.api.resources.Project; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.PersistenceManager; -import java.util.Arrays; -import java.util.Collection; - public final class Phases { public static Collection getPhaseClasses() { - return Arrays.asList( + return Arrays. asList( DecoratorsExecutor.class, MavenPhaseExecutor.class, MavenPluginsConfigurator.class, - PostJobsExecutor.class, SensorsExecutor.class, UpdateStatusJob.class - ); + PostJobsExecutor.class, SensorsExecutor.class, UpdateStatusJob.class, + InitializersExecutor.class); } private DecoratorsExecutor decoratorsExecutor; private MavenPhaseExecutor mavenPhaseExecutor; private MavenPluginsConfigurator mavenPluginsConfigurator; private PostJobsExecutor postJobsExecutor; + private InitializersExecutor initializersExecutor; private SensorsExecutor sensorsExecutor; private UpdateStatusJob updateStatusJob; private PersistenceManager persistenceManager; @@ -47,13 +48,14 @@ public final class Phases { private DefaultIndex index; public Phases(DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor, - MavenPluginsConfigurator mavenPluginsConfigurator, + MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, UpdateStatusJob updateStatusJob, PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index) { this.decoratorsExecutor = decoratorsExecutor; this.mavenPhaseExecutor = mavenPhaseExecutor; this.mavenPluginsConfigurator = mavenPluginsConfigurator; this.postJobsExecutor = postJobsExecutor; + this.initializersExecutor = initializersExecutor; this.sensorsExecutor = sensorsExecutor; this.updateStatusJob = updateStatusJob; this.persistenceManager = persistenceManager; @@ -67,6 +69,7 @@ public final class Phases { public void execute(Project project) { mavenPluginsConfigurator.execute(project); mavenPhaseExecutor.execute(project); + initializersExecutor.execute(project); persistenceManager.setDelayedMode(true); sensorsExecutor.execute(project, sensorContext); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractInitializer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Initializer.java similarity index 69% rename from sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractInitializer.java rename to sonar-plugin-api/src/main/java/org/sonar/api/batch/Initializer.java index 9c34f2869c9..fa52021df9f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractInitializer.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Initializer.java @@ -19,24 +19,28 @@ */ package org.sonar.api.batch; -import org.sonar.api.batch.Phase.Name; +import org.sonar.api.BatchExtension; import org.sonar.api.resources.Project; /** + *

+ * Initializer can execute external tool (like a Maven plugin), change project configuration. For example CoberturaMavenInitializer invokes + * the Codehaus Cobertura Mojo and sets path to Cobertura report according to Maven POM. + *

+ * + *

+ * Initializers are executed first and once during project analysis. + *

+ * * @since 2.6 */ -@Phase(name = Name.PRE) -public abstract class AbstractInitializer implements Sensor { +public abstract class Initializer implements BatchExtension, CheckProject { public boolean shouldExecuteOnProject(Project project) { return true; } - public void analyse(Project project, SensorContext context) { - prepare(project); - } - - public abstract void prepare(Project project); + public abstract void execute(Project project); @Override public String toString() { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java index ee50d58e49e..e8d9791d2cb 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java @@ -23,38 +23,41 @@ import org.sonar.api.BatchExtension; import org.sonar.api.resources.Project; /** - *

A Sensor is invoked once during the analysis of a project. The sensor can invoke a maven plugin, - * parse a flat file, connect to a web server... For example the Cobertura Sensor invokes the Codehaus Cobertura MOJO. - * Then the generated XML file is parsed and used to save the first-level of measures on resources - * (project, package or class).

- * - *

Sensors are executed first during project analysis. Sensor are generally used to add measure at the - * lowest level of the resource tree. A sensor can access and save measures on the whole tree of resources.

- * - *

A particular attention should be given to resource exclusion. Sonar already manages exclusions at file level : if - * you try to save a measure on a resource that is excluded in the settings, then Sonar will not save the measure. - * When handling a plugin or an external tool, you should make sure that exclusions are passed if you are going to get - * back consolidated data.

- * + *

+ * A Sensor is invoked once during the analysis of a project. The sensor can parse a flat file, connect to a web server... Sensor are + * generally used to add measure at the lowest level of the resource tree. A sensor can access and save measures on the whole tree of + * resources. + *

+ * + *

+ * For example the Cobertura Sensor parses Cobertura report and saves the first-level of measures on resources. + *

+ * + *

+ * A particular attention should be given to resource exclusion. Sonar already manages exclusions at file level : if you try to save a + * measure on a resource that is excluded in the settings, then Sonar will not save the measure. When handling a plugin or an external tool, + * you should make sure that exclusions are passed if you are going to get back consolidated data. + *

+ * * @since 1.10 */ public interface Sensor extends BatchExtension, CheckProject { /** * Sensors that depend upon Squid must declare the following method : - * - * - * @DependsUpon public String dependsUponSquidAnalysis() { - * return Sensor.FLAG_SQUID_ANALYSIS; - * } - * + * + *
+   * @DependsUpon
+   * public String dependsUponSquidAnalysis() {
+   *   return Sensor.FLAG_SQUID_ANALYSIS;
    * }
+   * 
*/ String FLAG_SQUID_ANALYSIS = "squid"; /** * The method that is going to be run when the sensor is called - * + * * @param project the project the sensor runs on * @param context the context */ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java index 11ea8d00f75..f58e5f7dc30 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java @@ -24,7 +24,13 @@ import org.sonar.api.batch.SupportedEnvironment; import org.sonar.api.resources.Project; /** - * Used for Sensors and PostJobs only. + * Can be used only for {@link org.sonar.api.batch.Initializer Initializers}, {@link org.sonar.api.batch.Sensor Sensors} and + * {@link org.sonar.api.batch.PostJob PostJobs}. + * + *

+ * If extension implements this interface, then it would be available only when Sonar executed from Maven. So we recommend to use this + * interface for Initializers instead of Sensors. + *

* * @since 1.10 */ diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/InitializerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/InitializerTest.java new file mode 100644 index 00000000000..7dd85e996dc --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/InitializerTest.java @@ -0,0 +1,43 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.batch; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +import org.junit.Test; +import org.sonar.api.resources.Project; + +public class InitializerTest { + + @Test + public void shouldBeExecutedByDefault() { + Project project = mock(Project.class); + assertThat(new FakeInitializer().shouldExecuteOnProject(project), is(true)); + } + + private class FakeInitializer extends Initializer { + @Override + public void execute(Project project) { + } + } + +} -- 2.39.5