]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4384 Add missing phases to Sonar profiling
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 12 Jun 2013 07:47:28 +0000 (09:47 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Wed, 12 Jun 2013 07:48:14 +0000 (09:48 +0200)
17 files changed:
sonar-batch/src/main/java/org/sonar/batch/events/BatchStepEvent.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/events/BatchStepHandler.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/phases/InitializerExecutionEvent.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java
sonar-batch/src/main/java/org/sonar/batch/phases/InitializersPhaseEvent.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseEvent.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/profiling/AbstractTimeProfiling.java
sonar-batch/src/main/java/org/sonar/batch/profiling/ModuleProfiling.java
sonar-batch/src/main/java/org/sonar/batch/profiling/PhaseProfiling.java
sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java
sonar-batch/src/main/java/org/sonar/batch/scan/JsonReport.java
sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/JsonReportTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializerExecutionHandler.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializersPhaseHandler.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/events/MavenPhaseHandler.java [new file with mode: 0644]

diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/BatchStepEvent.java b/sonar-batch/src/main/java/org/sonar/batch/events/BatchStepEvent.java
new file mode 100644 (file)
index 0000000..3003691
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.events;
+
+/**
+ * Generic event for some steps of project scan.
+ * @since 3.7
+ *
+ */
+public class BatchStepEvent extends BatchEvent<BatchStepHandler>
+    implements BatchStepHandler.BatchStepEvent {
+
+  private final boolean start;
+
+  private String stepName;
+
+  public BatchStepEvent(String stepName, boolean start) {
+    this.start = start;
+    this.stepName = stepName;
+  }
+
+  @Override
+  public String stepName() {
+    return stepName;
+  }
+
+  public final boolean isStart() {
+    return start;
+  }
+
+  public final boolean isEnd() {
+    return !start;
+  }
+
+  @Override
+  protected void dispatch(BatchStepHandler handler) {
+    handler.onBatchStep(this);
+  }
+
+  @Override
+  protected Class getType() {
+    return BatchStepHandler.class;
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/BatchStepHandler.java b/sonar-batch/src/main/java/org/sonar/batch/events/BatchStepHandler.java
new file mode 100644 (file)
index 0000000..2f4a395
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.events;
+
+import org.sonar.api.batch.events.EventHandler;
+
+/**
+ * @since 3.7
+ */
+public interface BatchStepHandler extends EventHandler {
+
+  /**
+   * This interface is not intended to be implemented by clients.
+   */
+  interface BatchStepEvent {
+
+    String stepName();
+
+    boolean isStart();
+
+    boolean isEnd();
+
+  }
+
+  /**
+   * Called before and after execution of each final step of project analysis
+   */
+  void onBatchStep(BatchStepEvent event);
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/InitializerExecutionEvent.java b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializerExecutionEvent.java
new file mode 100644 (file)
index 0000000..e85969d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.phases;
+
+import org.sonar.api.batch.Initializer;
+import org.sonar.api.batch.events.InitializerExecutionHandler;
+
+class InitializerExecutionEvent extends AbstractPhaseEvent<InitializerExecutionHandler>
+    implements org.sonar.api.batch.events.InitializerExecutionHandler.InitializerExecutionEvent {
+
+  private final Initializer initializer;
+
+  InitializerExecutionEvent(Initializer initializer, boolean start) {
+    super(start);
+    this.initializer = initializer;
+  }
+
+  public Initializer getInitializer() {
+    return initializer;
+  }
+
+  @Override
+  public void dispatch(InitializerExecutionHandler handler) {
+    handler.onInitializerExecution(this);
+  }
+
+  @Override
+  public Class getType() {
+    return InitializerExecutionHandler.class;
+  }
+
+}
index e3d391659b2694320248085161518a93f3c370b4..e57f5908a9e4a68f636e56719c5e57572b48aafe 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.batch.phases;
 
+import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -28,8 +29,9 @@ 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.scan.maven.MavenPluginExecutor;
+import org.sonar.batch.events.EventBus;
 import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.maven.MavenPluginExecutor;
 
 import java.util.Collection;
 
@@ -42,27 +44,34 @@ public class InitializersExecutor {
   private DefaultModuleFileSystem fs;
   private Project project;
   private BatchExtensionDictionnary selector;
+  private EventBus eventBus;
 
-  public InitializersExecutor(BatchExtensionDictionnary selector, Project project, DefaultModuleFileSystem fs, MavenPluginExecutor mavenExecutor) {
+  public InitializersExecutor(BatchExtensionDictionnary selector, Project project, DefaultModuleFileSystem fs, MavenPluginExecutor mavenExecutor, EventBus eventBus) {
     this.selector = selector;
     this.mavenExecutor = mavenExecutor;
     this.project = project;
     this.fs = fs;
+    this.eventBus = eventBus;
   }
 
   public void execute() {
     Collection<Initializer> initializers = selector.select(Initializer.class, project, true);
+    eventBus.fireEvent(new InitializersPhaseEvent(Lists.newArrayList(initializers), true));
     if (LOG.isDebugEnabled()) {
       LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> "));
     }
 
     for (Initializer initializer : initializers) {
+      eventBus.fireEvent(new InitializerExecutionEvent(initializer, true));
       executeMavenPlugin(initializer);
 
       TimeProfiler profiler = new TimeProfiler(LOG).start("Initializer " + initializer);
       initializer.execute(project);
       profiler.stop();
+      eventBus.fireEvent(new InitializerExecutionEvent(initializer, false));
     }
+
+    eventBus.fireEvent(new InitializersPhaseEvent(Lists.newArrayList(initializers), false));
   }
 
   private void executeMavenPlugin(Initializer sensor) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersPhaseEvent.java b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersPhaseEvent.java
new file mode 100644 (file)
index 0000000..a3d48fc
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.phases;
+
+import org.sonar.api.batch.Initializer;
+import org.sonar.api.batch.events.InitializersPhaseHandler;
+
+import java.util.List;
+
+class InitializersPhaseEvent extends AbstractPhaseEvent<InitializersPhaseHandler>
+    implements org.sonar.api.batch.events.InitializersPhaseHandler.InitializersPhaseEvent {
+
+  private final List<Initializer> initializers;
+
+  InitializersPhaseEvent(List<Initializer> initializers, boolean start) {
+    super(start);
+    this.initializers = initializers;
+  }
+
+  public List<Initializer> getInitializers() {
+    return initializers;
+  }
+
+  @Override
+  protected void dispatch(InitializersPhaseHandler handler) {
+    handler.onInitializersPhase(this);
+  }
+
+  @Override
+  protected Class getType() {
+    return InitializersPhaseHandler.class;
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseEvent.java b/sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseEvent.java
new file mode 100644 (file)
index 0000000..1552e50
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.phases;
+
+import org.sonar.api.batch.events.MavenPhaseHandler;
+
+class MavenPhaseEvent extends AbstractPhaseEvent<MavenPhaseHandler>
+    implements org.sonar.api.batch.events.MavenPhaseHandler.MavenPhaseEvent {
+
+  MavenPhaseEvent(boolean start) {
+    super(start);
+  }
+
+  @Override
+  protected void dispatch(MavenPhaseHandler handler) {
+    handler.onMavenPhase(this);
+  }
+
+  @Override
+  protected Class getType() {
+    return MavenPhaseHandler.class;
+  }
+
+}
index b690f19d43e4bc82dce39fcbe793be4135068c68..8b8dd93c7a98075e95732f994cecfb90dcd1232d 100644 (file)
@@ -24,6 +24,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.resources.Project;
+import org.sonar.batch.events.BatchStepEvent;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.DefaultIndex;
 import org.sonar.batch.index.PersistenceManager;
@@ -40,9 +41,9 @@ public final class PhaseExecutor {
   public static final Logger LOGGER = LoggerFactory.getLogger(PhaseExecutor.class);
 
   public static Collection<Class> getPhaseClasses() {
-    return Lists.<Class>newArrayList(DecoratorsExecutor.class, MavenPhaseExecutor.class, MavenPluginsConfigurator.class,
-      PostJobsExecutor.class, SensorsExecutor.class,
-      InitializersExecutor.class, ProjectInitializer.class, UpdateStatusJob.class);
+    return Lists.<Class> newArrayList(DecoratorsExecutor.class, MavenPhaseExecutor.class, MavenPluginsConfigurator.class,
+        PostJobsExecutor.class, SensorsExecutor.class,
+        InitializersExecutor.class, ProjectInitializer.class, UpdateStatusJob.class);
   }
 
   private EventBus eventBus;
@@ -63,11 +64,11 @@ public final class PhaseExecutor {
   private final JsonReport jsonReport;
 
   public PhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
-                       MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
-                       PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
-                       PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
-                       EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi,
-                       ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport) {
+      MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
+      PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
+      PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
+      EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi,
+      ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport) {
     this.phases = phases;
     this.decoratorsExecutor = decoratorsExecutor;
     this.mavenPhaseExecutor = mavenPhaseExecutor;
@@ -87,12 +88,12 @@ public final class PhaseExecutor {
   }
 
   public PhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
-                       MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
-                       PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
-                       PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
-                       EventBus eventBus, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport) {
+      MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
+      PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
+      PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
+      EventBus eventBus, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport) {
     this(phases, decoratorsExecutor, mavenPhaseExecutor, mavenPluginsConfigurator, initializersExecutor, postJobsExecutor,
-      sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, persisters, fsLogger, jsonReport);
+        sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, persisters, fsLogger, jsonReport);
   }
 
   /**
@@ -100,37 +101,33 @@ public final class PhaseExecutor {
    */
   public void execute(Project module) {
     pi.execute(module);
+
     eventBus.fireEvent(new ProjectAnalysisEvent(module, true));
-    if (phases.isEnabled(Phases.Phase.MAVEN)) {
-      mavenPluginsConfigurator.execute(module);
-      mavenPhaseExecutor.execute(module);
-    }
-    if (phases.isEnabled(Phases.Phase.INIT)) {
-      initializersExecutor.execute();
-      fsLogger.log();
-    }
+
+    executeMavenPhase(module);
+
+    executeInitializersPhase();
 
     persistenceManager.setDelayedMode(true);
+
     if (phases.isEnabled(Phases.Phase.SENSOR)) {
       sensorsExecutor.execute(sensorContext);
     }
+
     if (phases.isEnabled(Phases.Phase.DECORATOR)) {
       decoratorsExecutor.execute();
     }
+
+    eventBus.fireEvent(new BatchStepEvent("Save measures", true));
     persistenceManager.dump();
+    eventBus.fireEvent(new BatchStepEvent("Save measures", false));
     persistenceManager.setDelayedMode(false);
 
     if (module.isRoot()) {
       jsonReport.execute();
 
-      LOGGER.info("Store results in database");
-      for (ScanPersister persister : persisters) {
-        LOGGER.debug("Execute {}", persister.getClass().getName());
-        persister.persist();
-      }
-      if (updateStatusJob != null) {
-        updateStatusJob.execute();
-      }
+      executePersisters();
+      updateStatusJob();
       if (phases.isEnabled(Phases.Phase.POSTJOB)) {
         postJobsExecutor.execute(sensorContext);
       }
@@ -139,8 +136,44 @@ public final class PhaseExecutor {
     eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
   }
 
+  private void executePersisters() {
+    LOGGER.info("Store results in database");
+    eventBus.fireEvent(new BatchStepEvent("Persisters", true));
+    for (ScanPersister persister : persisters) {
+      LOGGER.debug("Execute {}", persister.getClass().getName());
+      persister.persist();
+    }
+    eventBus.fireEvent(new BatchStepEvent("Persisters", false));
+  }
+
+  private void updateStatusJob() {
+    if (updateStatusJob != null) {
+      eventBus.fireEvent(new BatchStepEvent("Update status job", true));
+      updateStatusJob.execute();
+      eventBus.fireEvent(new BatchStepEvent("Update status job", false));
+    }
+  }
+
+  private void executeInitializersPhase() {
+    if (phases.isEnabled(Phases.Phase.INIT)) {
+      initializersExecutor.execute();
+      fsLogger.log();
+    }
+  }
+
+  private void executeMavenPhase(Project module) {
+    if (phases.isEnabled(Phases.Phase.MAVEN)) {
+      eventBus.fireEvent(new MavenPhaseEvent(true));
+      mavenPluginsConfigurator.execute(module);
+      mavenPhaseExecutor.execute(module);
+      eventBus.fireEvent(new MavenPhaseEvent(false));
+    }
+  }
+
   private void cleanMemory() {
+    eventBus.fireEvent(new BatchStepEvent("Clean memory", true));
     persistenceManager.clear();
     index.clear();
+    eventBus.fireEvent(new BatchStepEvent("Clean memory", false));
   }
 }
index a610520ef5f9b08e51a154212f5629fdb3cda6d0..badcec28122c32a9b1980b6175651fcc74425fc4 100644 (file)
@@ -25,7 +25,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 public abstract class AbstractTimeProfiling {
 
@@ -64,16 +66,20 @@ public abstract class AbstractTimeProfiling {
     this.setTotalTime(this.totalTime() + other.totalTime());
   }
 
-  static <G extends AbstractTimeProfiling> List<G> sortByDescendingTotalTime(Collection<G> unsorted) {
-    List<G> result = new ArrayList<G>(unsorted.size());
-    result.addAll(unsorted);
-    Collections.sort(result, new Comparator<G>() {
+  static <G extends AbstractTimeProfiling> Map<Object, G> sortByDescendingTotalTime(Map<?, G> unsorted) {
+    List<Map.Entry<?, G>> entries =
+        new ArrayList<Map.Entry<?, G>>(unsorted.entrySet());
+    Collections.sort(entries, new Comparator<Map.Entry<?, G>>() {
       @Override
-      public int compare(G o1, G o2) {
-        return Long.valueOf(o2.totalTime()).compareTo(o1.totalTime());
+      public int compare(Map.Entry<?, G> o1, Map.Entry<?, G> o2) {
+        return Long.valueOf(o2.getValue().totalTime()).compareTo(o1.getValue().totalTime());
       }
     });
-    return result;
+    Map<Object, G> sortedMap = new LinkedHashMap<Object, G>();
+    for (Map.Entry<?, G> entry : entries) {
+      sortedMap.put(entry.getKey(), entry.getValue());
+    }
+    return sortedMap;
   }
 
   static <G extends AbstractTimeProfiling> List<G> truncate(Collection<G> sortedList) {
@@ -95,4 +101,12 @@ public abstract class AbstractTimeProfiling {
     PhasesSumUpTimeProfiler.println(msg);
   }
 
+  protected void println(String text, Double percent, AbstractTimeProfiling phaseProfiling) {
+    PhasesSumUpTimeProfiler.println(text, percent, phaseProfiling);
+  }
+
+  protected void println(String text, AbstractTimeProfiling phaseProfiling) {
+    println(text, null, phaseProfiling);
+  }
+
 }
index e1b48fcba697388cfedf70fac27bd0c8305f34e8..e7ba4104cb03089306e99ef8d582107aef51a983 100644 (file)
  */
 package org.sonar.batch.profiling;
 
+import com.google.common.collect.Maps;
 import org.sonar.api.resources.Project;
-import org.sonar.api.utils.TimeUtils;
 import org.sonar.batch.phases.Phases;
 import org.sonar.batch.phases.Phases.Phase;
 
 import javax.annotation.CheckForNull;
 
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
 public class ModuleProfiling extends AbstractTimeProfiling {
 
   private Map<Phases.Phase, PhaseProfiling> profilingPerPhase = new HashMap<Phases.Phase, PhaseProfiling>();
+  private Map<String, ItemProfiling> profilingPerBatchStep = new LinkedHashMap<String, ItemProfiling>();
   private Clock clock;
   private Project module;
 
@@ -53,24 +55,32 @@ public class ModuleProfiling extends AbstractTimeProfiling {
     return profilingPerPhase.get(phase);
   }
 
+  public ItemProfiling getProfilingPerBatchStep(String stepName) {
+    return profilingPerBatchStep.get(stepName);
+  }
+
   public void addPhaseProfiling(Phase phase) {
     profilingPerPhase.put(phase, PhaseProfiling.create(clock, phase));
   }
 
+  public void addBatchStepProfiling(String stepName) {
+    profilingPerBatchStep.put(stepName, new ItemProfiling(clock, stepName));
+  }
+
   public void dump() {
     double percent = this.totalTime() / 100.0;
-    for (PhaseProfiling phaseProfiling : sortByDescendingTotalTime(profilingPerPhase.values())) {
-      StringBuilder sb = new StringBuilder();
-      sb.append(" * ").append(phaseProfiling.phase()).append(" execution time: ").append(phaseProfiling.totalTimeAsString())
-          .append(" (").append((int) (phaseProfiling.totalTime() / percent)).append("%)");
-      println(sb.toString());
+    Map<Object, AbstractTimeProfiling> categories = Maps.newLinkedHashMap();
+    categories.putAll(profilingPerPhase);
+    categories.putAll(profilingPerBatchStep);
+
+    for (Map.Entry<Object, AbstractTimeProfiling> batchStep : sortByDescendingTotalTime(categories).entrySet()) {
+      println(" * " + batchStep.getKey() + " execution time: ", percent, batchStep.getValue());
     }
+    // Breakdown per phase
     for (Phase phase : Phases.Phase.values()) {
-      if (profilingPerPhase.containsKey(phase)) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("\n * ").append(phase).append(" execution time breakdown: ")
-            .append(TimeUtils.formatDuration(getProfilingPerPhase(phase).totalTime()));
-        println(sb.toString());
+      if (profilingPerPhase.containsKey(phase) && getProfilingPerPhase(phase).hasItems()) {
+        println("");
+        println(" * " + phase + " execution time breakdown: ", getProfilingPerPhase(phase));
         getProfilingPerPhase(phase).dump();
       }
     }
@@ -84,6 +94,12 @@ public class ModuleProfiling extends AbstractTimeProfiling {
       }
       this.getProfilingPerPhase(entry.getKey()).merge(entry.getValue());
     }
+    for (Map.Entry<String, ItemProfiling> entry : other.profilingPerBatchStep.entrySet()) {
+      if (!this.profilingPerBatchStep.containsKey(entry.getKey())) {
+        profilingPerBatchStep.put(entry.getKey(), new ItemProfiling(clock, entry.getKey()));
+      }
+      this.getProfilingPerBatchStep(entry.getKey()).add(entry.getValue());
+    }
   }
 
 }
index 8bb5bcbdacd884da880136bb405a4fc45156db83..45ba18a17bbdd551b2235e4d41fa2119b4e03aee 100644 (file)
@@ -47,6 +47,10 @@ public class PhaseProfiling extends AbstractTimeProfiling {
     return phase;
   }
 
+  public boolean hasItems() {
+    return !profilingPerItem.isEmpty();
+  }
+
   public ItemProfiling getProfilingPerItem(Object item) {
     String stringOrSimpleName = toStringOrSimpleName(item);
     return profilingPerItem.get(stringOrSimpleName);
@@ -73,11 +77,8 @@ public class PhaseProfiling extends AbstractTimeProfiling {
 
   public void dump() {
     double percent = this.totalTime() / 100.0;
-    for (ItemProfiling itemProfiling : truncate(sortByDescendingTotalTime(profilingPerItem.values()))) {
-      StringBuilder sb = new StringBuilder();
-      sb.append("   o ").append(itemProfiling.itemName()).append(": ").append(itemProfiling.totalTimeAsString())
-          .append(" (").append((int) (itemProfiling.totalTime() / percent)).append("%)");
-      println(sb.toString());
+    for (ItemProfiling itemProfiling : truncate(sortByDescendingTotalTime(profilingPerItem).values())) {
+      println("   o " + itemProfiling.itemName() + ": ", percent, itemProfiling);
     }
   }
 
index 6137b79875eece0e4f894d54a119edd2617ba494..6aebf4f1b86c71f5d7ba73185404065f14054558 100644 (file)
@@ -21,11 +21,15 @@ package org.sonar.batch.profiling;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.events.DecoratorExecutionHandler;
 import org.sonar.api.batch.events.DecoratorsPhaseHandler;
+import org.sonar.api.batch.events.InitializerExecutionHandler;
+import org.sonar.api.batch.events.InitializersPhaseHandler;
+import org.sonar.api.batch.events.MavenPhaseHandler;
 import org.sonar.api.batch.events.PostJobExecutionHandler;
 import org.sonar.api.batch.events.PostJobsPhaseHandler;
 import org.sonar.api.batch.events.ProjectAnalysisHandler;
@@ -33,9 +37,10 @@ import org.sonar.api.batch.events.SensorExecutionHandler;
 import org.sonar.api.batch.events.SensorsPhaseHandler;
 import org.sonar.api.resources.Project;
 import org.sonar.api.utils.TimeUtils;
+import org.sonar.batch.events.BatchStepHandler;
 import org.sonar.batch.phases.Phases;
 
-import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -44,15 +49,17 @@ import static org.sonar.batch.profiling.AbstractTimeProfiling.sortByDescendingTo
 import static org.sonar.batch.profiling.AbstractTimeProfiling.truncate;
 
 public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorExecutionHandler, DecoratorExecutionHandler, PostJobExecutionHandler, DecoratorsPhaseHandler,
-    SensorsPhaseHandler, PostJobsPhaseHandler {
+    SensorsPhaseHandler, PostJobsPhaseHandler, MavenPhaseHandler, InitializersPhaseHandler, InitializerExecutionHandler, BatchStepHandler {
 
   static Logger LOG = LoggerFactory.getLogger(PhasesSumUpTimeProfiler.class);
+  private static final int TEXT_RIGHT_PAD = 60;
+  private static final int TIME_LEFT_PAD = 10;
 
   @VisibleForTesting
   ModuleProfiling currentModuleProfiling;
   @VisibleForTesting
   ModuleProfiling totalProfiling;
-  private List<ModuleProfiling> modulesProfilings = new ArrayList<ModuleProfiling>();
+  private Map<Project, ModuleProfiling> modulesProfilings = new HashMap<Project, ModuleProfiling>();
   private DecoratorsProfiler decoratorsProfiler;
 
   private Clock clock;
@@ -65,6 +72,15 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx
     LOG.info(msg);
   }
 
+  static void println(String text, Double percent, AbstractTimeProfiling phaseProfiling) {
+    StringBuilder sb = new StringBuilder();
+    sb.append(StringUtils.rightPad(text, TEXT_RIGHT_PAD)).append(StringUtils.leftPad(phaseProfiling.totalTimeAsString(), TIME_LEFT_PAD));
+    if (percent != null) {
+      sb.append(" (").append((int) (phaseProfiling.totalTime() / percent)).append("%)");
+    }
+    println(sb.toString());
+  }
+
   @VisibleForTesting
   PhasesSumUpTimeProfiler(Clock clock) {
     this.clock = clock;
@@ -80,11 +96,15 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx
     }
     else {
       currentModuleProfiling.stop();
-      modulesProfilings.add(currentModuleProfiling);
+      modulesProfilings.put(module, currentModuleProfiling);
       long moduleTotalTime = currentModuleProfiling.totalTime();
-      println("\n -------- Profiling of module " + module.getName() + ": " + TimeUtils.formatDuration(moduleTotalTime) + " --------\n");
+      println("");
+      println(" -------- Profiling of module " + module.getName() + ": " + TimeUtils.formatDuration(moduleTotalTime) + " --------");
+      println("");
       currentModuleProfiling.dump();
-      println("\n -------- End of profiling of module " + module.getName() + " --------\n");
+      println("");
+      println(" -------- End of profiling of module " + module.getName() + " --------");
+      println("");
       totalProfiling.merge(currentModuleProfiling);
       if (module.isRoot() && !module.getModules().isEmpty()) {
         dumpTotalExecutionSummary();
@@ -95,18 +115,19 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx
   private void dumpTotalExecutionSummary() {
     totalProfiling.stop();
     long totalTime = totalProfiling.totalTime();
-    println("\n ======== Profiling of total execution: " + TimeUtils.formatDuration(totalTime) + " ========\n");
+    println("");
+    println(" ======== Profiling of total execution: " + TimeUtils.formatDuration(totalTime) + " ========");
+    println("");
     println(" * Module execution time breakdown: ");
     double percent = totalTime / 100.0;
-    for (ModuleProfiling modulesProfiling : truncate(sortByDescendingTotalTime(modulesProfilings))) {
-      StringBuilder sb = new StringBuilder();
-      sb.append("   o ").append(modulesProfiling.moduleName()).append(" execution time: ").append(modulesProfiling.totalTimeAsString())
-          .append(" (").append((int) (modulesProfiling.totalTime() / percent)).append("%)");
-      println(sb.toString());
+    for (ModuleProfiling modulesProfiling : truncate(sortByDescendingTotalTime(modulesProfilings).values())) {
+      println("   o " + modulesProfiling.moduleName() + " execution time: ", percent, modulesProfiling);
     }
     println("");
     totalProfiling.dump();
-    println("\n ======== End of profiling of total execution ========\n");
+    println("");
+    println(" ======== End of profiling of total execution ========");
+    println("");
   }
 
   public void onSensorsPhase(SensorsPhaseEvent event) {
@@ -170,6 +191,46 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx
     }
   }
 
+  @Override
+  public void onMavenPhase(MavenPhaseEvent event) {
+    if (event.isStart()) {
+      currentModuleProfiling.addPhaseProfiling(Phases.Phase.MAVEN);
+    }
+    else {
+      currentModuleProfiling.getProfilingPerPhase(Phases.Phase.MAVEN).stop();
+    }
+  }
+
+  @Override
+  public void onInitializersPhase(InitializersPhaseEvent event) {
+    if (event.isStart()) {
+      currentModuleProfiling.addPhaseProfiling(Phases.Phase.INIT);
+    }
+    else {
+      currentModuleProfiling.getProfilingPerPhase(Phases.Phase.INIT).stop();
+    }
+  }
+
+  @Override
+  public void onInitializerExecution(InitializerExecutionEvent event) {
+    PhaseProfiling profiling = currentModuleProfiling.getProfilingPerPhase(Phases.Phase.INIT);
+    if (event.isStart()) {
+      profiling.newItemProfiling(event.getInitializer());
+    } else {
+      profiling.getProfilingPerItem(event.getInitializer()).stop();
+    }
+  }
+
+  @Override
+  public void onBatchStep(BatchStepEvent event) {
+    if (event.isStart()) {
+      currentModuleProfiling.addBatchStepProfiling(event.stepName());
+    }
+    else {
+      currentModuleProfiling.getProfilingPerBatchStep(event.stepName()).stop();
+    }
+  }
+
   class DecoratorsProfiler {
     List<Decorator> decorators = Lists.newArrayList();
     Map<Decorator, Long> durations = new IdentityHashMap<Decorator, Long>();
index 33b2097ee3fe608a109845bb228ef09a69552bef..49bfcc5fa02941b98ec95262781df9c7c8b577e4 100644 (file)
@@ -34,10 +34,16 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.scan.filesystem.ModuleFileSystem;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.SonarException;
+import org.sonar.batch.events.BatchStepEvent;
+import org.sonar.batch.events.EventBus;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.core.i18n.RuleI18nManager;
 
-import java.io.*;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
 import java.util.Locale;
 import java.util.Set;
 
@@ -55,18 +61,22 @@ public class JsonReport implements BatchComponent {
   private final Server server;
   private final RuleI18nManager ruleI18nManager;
   private final IssueCache issueCache;
+  private EventBus eventBus;
 
-  public JsonReport(Settings settings, ModuleFileSystem fileSystem, Server server, RuleI18nManager ruleI18nManager, IssueCache issueCache) {
+  public JsonReport(Settings settings, ModuleFileSystem fileSystem, Server server, RuleI18nManager ruleI18nManager, IssueCache issueCache, EventBus eventBus) {
     this.settings = settings;
     this.fileSystem = fileSystem;
     this.server = server;
     this.ruleI18nManager = ruleI18nManager;
     this.issueCache = issueCache;
+    this.eventBus = eventBus;
   }
 
   public void execute() {
     if (settings.getBoolean(CoreProperties.DRY_RUN)) {
+      eventBus.fireEvent(new BatchStepEvent("JSON report", true));
       exportResults();
+      eventBus.fireEvent(new BatchStepEvent("JSON report", false));
     }
   }
 
@@ -114,19 +124,19 @@ public class JsonReport implements BatchComponent {
     for (DefaultIssue issue : getIssues()) {
       if (issue.resolution() == null) {
         json
-          .beginObject()
-          .name("key").value(issue.key())
-          .name("component").value(issue.componentKey())
-          .name("line").value(issue.line())
-          .name("message").value(issue.message())
-          .name("severity").value(issue.severity())
-          .name("rule").value(issue.ruleKey().toString())
-          .name("status").value(issue.status())
-          .name("resolution").value(issue.resolution())
-          .name("isNew").value(issue.isNew())
-          .name("reporter").value(issue.reporter())
-          .name("assignee").value(issue.assignee())
-          .name("effortToFix").value(issue.effortToFix());
+            .beginObject()
+            .name("key").value(issue.key())
+            .name("component").value(issue.componentKey())
+            .name("line").value(issue.line())
+            .name("message").value(issue.message())
+            .name("severity").value(issue.severity())
+            .name("rule").value(issue.ruleKey().toString())
+            .name("status").value(issue.status())
+            .name("resolution").value(issue.resolution())
+            .name("isNew").value(issue.isNew())
+            .name("reporter").value(issue.reporter())
+            .name("assignee").value(issue.assignee())
+            .name("effortToFix").value(issue.effortToFix());
         if (issue.creationDate() != null) {
           json.name("creationDate").value(DateUtils.formatDateTime(issue.creationDate()));
         }
@@ -148,9 +158,9 @@ public class JsonReport implements BatchComponent {
     json.name("components").beginArray();
     for (String componentKey : componentKeys) {
       json
-        .beginObject()
-        .name("key").value(componentKey)
-        .endObject();
+          .beginObject()
+          .name("key").value(componentKey)
+          .endObject();
     }
     json.endArray();
   }
@@ -159,12 +169,12 @@ public class JsonReport implements BatchComponent {
     json.name("rules").beginArray();
     for (RuleKey ruleKey : ruleKeys) {
       json
-        .beginObject()
-        .name("key").value(ruleKey.toString())
-        .name("rule").value(ruleKey.rule())
-        .name("repository").value(ruleKey.repository())
-        .name("name").value(getRuleName(ruleKey))
-        .endObject();
+          .beginObject()
+          .name("key").value(ruleKey.toString())
+          .name("rule").value(ruleKey.rule())
+          .name("repository").value(ruleKey.repository())
+          .name("name").value(getRuleName(ruleKey))
+          .endObject();
     }
     json.endArray();
   }
index b7a56ce60a4981ce73c8f42731582fe94faf519a..9c504dbe35ab6aa769f0597bbef2295297e89c93 100644 (file)
@@ -23,11 +23,15 @@ import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.batch.Decorator;
 import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.Initializer;
 import org.sonar.api.batch.PostJob;
 import org.sonar.api.batch.Sensor;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.events.DecoratorExecutionHandler;
 import org.sonar.api.batch.events.DecoratorsPhaseHandler;
+import org.sonar.api.batch.events.InitializerExecutionHandler;
+import org.sonar.api.batch.events.InitializersPhaseHandler;
+import org.sonar.api.batch.events.MavenPhaseHandler;
 import org.sonar.api.batch.events.PostJobExecutionHandler;
 import org.sonar.api.batch.events.PostJobsPhaseHandler;
 import org.sonar.api.batch.events.ProjectAnalysisHandler;
@@ -38,6 +42,7 @@ import org.sonar.api.batch.events.SensorsPhaseHandler;
 import org.sonar.api.batch.events.SensorsPhaseHandler.SensorsPhaseEvent;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
+import org.sonar.batch.events.BatchStepHandler;
 import org.sonar.batch.phases.Phases.Phase;
 
 import java.util.Arrays;
@@ -67,6 +72,8 @@ public class PhasesSumUpTimeProfilerTest {
 
     fakeAnalysis(profiler, project);
 
+    assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.MAVEN).totalTime()).isEqualTo(4L);
+    assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.DECORATOR).getProfilingPerItem(new FakeDecorator1()).totalTime()).isEqualTo(20L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L);
@@ -84,11 +91,15 @@ public class PhasesSumUpTimeProfilerTest {
     fakeAnalysis(profiler, moduleB);
     fakeAnalysis(profiler, project);
 
+    assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.MAVEN).totalTime()).isEqualTo(4L);
+    assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.DECORATOR).getProfilingPerItem(new FakeDecorator1()).totalTime()).isEqualTo(20L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.DECORATOR).getProfilingPerItem(new FakeDecorator2()).totalTime()).isEqualTo(10L);
     assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L);
 
+    assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.MAVEN).totalTime()).isEqualTo(12L);
+    assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(21L);
     assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(30L);
     assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.DECORATOR).getProfilingPerItem(new FakeDecorator1()).totalTime()).isEqualTo(60L);
     assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.DECORATOR).getProfilingPerItem(new FakeDecorator2()).totalTime()).isEqualTo(30L);
@@ -136,9 +147,12 @@ public class PhasesSumUpTimeProfilerTest {
   private void fakeAnalysis(PhasesSumUpTimeProfiler profiler, final Project module) throws InterruptedException {
     // Start of moduleA
     profiler.onProjectAnalysis(projectEvent(module, true));
+    mavenPhase(profiler);
+    initializerPhase(profiler);
     sensorPhase(profiler);
     decoratorPhase(profiler);
     postJobPhase(profiler);
+    batchStep(profiler);
     // End of moduleA
     profiler.onProjectAnalysis(projectEvent(module, false));
   }
@@ -172,6 +186,35 @@ public class PhasesSumUpTimeProfilerTest {
     profiler.onDecoratorsPhase(decoratorsEvent(false));
   }
 
+  private void batchStep(PhasesSumUpTimeProfiler profiler) throws InterruptedException {
+    // Start of batch step
+    profiler.onBatchStep(batchStepEvent(true, "Free memory"));
+    clock.sleep(9);
+    // End of batch step
+    profiler.onBatchStep(batchStepEvent(false, "Free memory"));
+  }
+
+  private void mavenPhase(PhasesSumUpTimeProfiler profiler) throws InterruptedException {
+    // Start of maven phase
+    profiler.onMavenPhase(mavenEvent(true));
+    clock.sleep(4);
+    // End of maven phase
+    profiler.onMavenPhase(mavenEvent(false));
+  }
+
+  private void initializerPhase(PhasesSumUpTimeProfiler profiler) throws InterruptedException {
+    Initializer initializer = new FakeInitializer();
+    // Start of initializer phase
+    profiler.onInitializersPhase(initializersEvent(true));
+    // Start of an initializer
+    profiler.onInitializerExecution(initializerEvent(initializer, true));
+    clock.sleep(7);
+    // End of an initializer
+    profiler.onInitializerExecution(initializerEvent(initializer, false));
+    // End of initializer phase
+    profiler.onInitializersPhase(initializersEvent(false));
+  }
+
   private void sensorPhase(PhasesSumUpTimeProfiler profiler) throws InterruptedException {
     Sensor sensor = new FakeSensor();
     // Start of sensor phase
@@ -218,6 +261,26 @@ public class PhasesSumUpTimeProfilerTest {
     };
   }
 
+  private InitializerExecutionHandler.InitializerExecutionEvent initializerEvent(final Initializer initializer, final boolean start) {
+    return new InitializerExecutionHandler.InitializerExecutionEvent() {
+
+      @Override
+      public boolean isStart() {
+        return start;
+      }
+
+      @Override
+      public boolean isEnd() {
+        return !start;
+      }
+
+      @Override
+      public Initializer getInitializer() {
+        return initializer;
+      }
+    };
+  }
+
   private DecoratorExecutionHandler.DecoratorExecutionEvent decoratorEvent(final Decorator decorator, final boolean start) {
     return new DecoratorExecutionHandler.DecoratorExecutionEvent() {
 
@@ -278,6 +341,61 @@ public class PhasesSumUpTimeProfilerTest {
     };
   }
 
+  private BatchStepHandler.BatchStepEvent batchStepEvent(final boolean start, final String stepName) {
+    return new BatchStepHandler.BatchStepEvent() {
+
+      @Override
+      public boolean isStart() {
+        return start;
+      }
+
+      @Override
+      public boolean isEnd() {
+        return !start;
+      }
+
+      @Override
+      public String stepName() {
+        return stepName;
+      }
+    };
+  }
+
+  private MavenPhaseHandler.MavenPhaseEvent mavenEvent(final boolean start) {
+    return new MavenPhaseHandler.MavenPhaseEvent() {
+
+      @Override
+      public boolean isStart() {
+        return start;
+      }
+
+      @Override
+      public boolean isEnd() {
+        return !start;
+      }
+    };
+  }
+
+  private InitializersPhaseHandler.InitializersPhaseEvent initializersEvent(final boolean start) {
+    return new InitializersPhaseHandler.InitializersPhaseEvent() {
+
+      @Override
+      public boolean isStart() {
+        return start;
+      }
+
+      @Override
+      public boolean isEnd() {
+        return !start;
+      }
+
+      @Override
+      public List<Initializer> getInitializers() {
+        return null;
+      }
+    };
+  }
+
   private PostJobsPhaseHandler.PostJobsPhaseEvent postJobsEvent(final boolean start) {
     return new PostJobsPhaseHandler.PostJobsPhaseEvent() {
 
@@ -342,24 +460,40 @@ public class PhasesSumUpTimeProfilerTest {
     public void analyse(Project project, SensorContext context) {
     }
 
+    @Override
+    public boolean shouldExecuteOnProject(Project project) {
+      return true;
+    }
+  }
+
+  public class FakeInitializer extends Initializer {
+    @Override
+    public void execute(Project project) {
+    }
+
+    @Override
     public boolean shouldExecuteOnProject(Project project) {
       return true;
     }
   }
 
   public class FakeDecorator1 implements Decorator {
+    @Override
     public void decorate(Resource resource, DecoratorContext context) {
     }
 
+    @Override
     public boolean shouldExecuteOnProject(Project project) {
       return true;
     }
   }
 
   public class FakeDecorator2 implements Decorator {
+    @Override
     public void decorate(Resource resource, DecoratorContext context) {
     }
 
+    @Override
     public boolean shouldExecuteOnProject(Project project) {
       return true;
     }
index 3f92ad420f3334e0bf3a7d13655fca1b05fb46cf..51ad93918b4830fffae08b9496993535cf084c92 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.scan.filesystem.ModuleFileSystem;
 import org.sonar.api.utils.DateUtils;
+import org.sonar.batch.events.EventBus;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.core.i18n.RuleI18nManager;
 import org.sonar.test.TestUtils;
@@ -69,61 +70,61 @@ public class JsonReportTest {
 
     settings = new Settings();
     settings.setProperty(CoreProperties.DRY_RUN, true);
-    jsonReport = new JsonReport(settings, fileSystem, server, ruleI18nManager, issueCache);
+    jsonReport = new JsonReport(settings, fileSystem, server, ruleI18nManager, issueCache, mock(EventBus.class));
   }
 
   @Test
   public void should_write_json() throws JSONException {
     DefaultIssue issue = new DefaultIssue()
-      .setKey("200")
-      .setComponentKey("struts:org.apache.struts.Action")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
-      .setMessage("There are 2 cycles")
-      .setSeverity("MINOR")
-      .setStatus(Issue.STATUS_OPEN)
-      .setResolution(null)
-      .setLine(1)
-      .setEffortToFix(3.14)
-      .setReporter("julien")
-      .setAssignee("simon")
-      .setCreationDate(DateUtils.parseDate("2013-04-24"))
-      .setUpdateDate(DateUtils.parseDate("2013-04-25"))
-      .setNew(false);
+        .setKey("200")
+        .setComponentKey("struts:org.apache.struts.Action")
+        .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
+        .setMessage("There are 2 cycles")
+        .setSeverity("MINOR")
+        .setStatus(Issue.STATUS_OPEN)
+        .setResolution(null)
+        .setLine(1)
+        .setEffortToFix(3.14)
+        .setReporter("julien")
+        .setAssignee("simon")
+        .setCreationDate(DateUtils.parseDate("2013-04-24"))
+        .setUpdateDate(DateUtils.parseDate("2013-04-25"))
+        .setNew(false);
     when(ruleI18nManager.getName("squid", "AvoidCycles", Locale.getDefault())).thenReturn("Avoid Cycles");
-    when(jsonReport.getIssues()).thenReturn(Lists.<DefaultIssue>newArrayList(issue));
+    when(jsonReport.getIssues()).thenReturn(Lists.<DefaultIssue> newArrayList(issue));
 
     StringWriter writer = new StringWriter();
     jsonReport.writeJson(writer);
 
     JSONAssert.assertEquals(TestUtils.getResourceContent("/org/sonar/batch/scan/JsonReportTest/report.json"),
-      writer.toString(), false);
+        writer.toString(), false);
   }
 
   @Test
   public void should_exclude_resolved_issues() throws JSONException {
     DefaultIssue issue = new DefaultIssue()
-      .setKey("200")
-      .setComponentKey("struts:org.apache.struts.Action")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
-      .setStatus(Issue.STATUS_CLOSED)
-      .setResolution(Issue.RESOLUTION_FIXED)
-      .setCreationDate(DateUtils.parseDate("2013-04-24"))
-      .setUpdateDate(DateUtils.parseDate("2013-04-25"))
-      .setCloseDate(DateUtils.parseDate("2013-04-26"))
-      .setNew(false);
+        .setKey("200")
+        .setComponentKey("struts:org.apache.struts.Action")
+        .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
+        .setStatus(Issue.STATUS_CLOSED)
+        .setResolution(Issue.RESOLUTION_FIXED)
+        .setCreationDate(DateUtils.parseDate("2013-04-24"))
+        .setUpdateDate(DateUtils.parseDate("2013-04-25"))
+        .setCloseDate(DateUtils.parseDate("2013-04-26"))
+        .setNew(false);
     when(ruleI18nManager.getName("squid", "AvoidCycles", Locale.getDefault())).thenReturn("Avoid Cycles");
-    when(jsonReport.getIssues()).thenReturn(Lists.<DefaultIssue>newArrayList(issue));
+    when(jsonReport.getIssues()).thenReturn(Lists.<DefaultIssue> newArrayList(issue));
 
     StringWriter writer = new StringWriter();
     jsonReport.writeJson(writer);
 
     JSONAssert.assertEquals(TestUtils.getResourceContent("/org/sonar/batch/scan/JsonReportTest/report-without-resolved-issues.json"),
-      writer.toString(), false);
+        writer.toString(), false);
   }
 
   @Test
   public void should_ignore_components_without_issue() throws JSONException {
-    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue>emptyList());
+    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue> emptyList());
 
     StringWriter writer = new StringWriter();
     jsonReport.writeJson(writer);
@@ -137,7 +138,7 @@ public class JsonReportTest {
 
     Rule rule = Rule.create("squid", "AvoidCycles");
     when(ruleI18nManager.getName(rule, Locale.getDefault())).thenReturn("Avoid Cycles");
-    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue>emptyList());
+    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue> emptyList());
 
     settings.setProperty("sonar.report.export.path", "output.json");
     when(fileSystem.workingDir()).thenReturn(sonarDirectory);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializerExecutionHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializerExecutionHandler.java
new file mode 100644 (file)
index 0000000..951d053
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.api.batch.events;
+
+import org.sonar.api.batch.Initializer;
+
+/**
+ * @since 3.7
+ */
+public interface InitializerExecutionHandler extends EventHandler {
+
+  /**
+   * This interface is not intended to be implemented by clients.
+   */
+  interface InitializerExecutionEvent {
+
+    Initializer getInitializer();
+
+    boolean isStart();
+
+    boolean isEnd();
+
+  }
+
+  /**
+   * Called before and after execution of {@link Initializer}.
+   */
+  void onInitializerExecution(InitializerExecutionEvent event);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializersPhaseHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializersPhaseHandler.java
new file mode 100644 (file)
index 0000000..7f57647
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.api.batch.events;
+
+import org.sonar.api.batch.Initializer;
+
+import java.util.List;
+
+/**
+ * @since 3.7
+ */
+public interface InitializersPhaseHandler extends EventHandler {
+
+  /**
+   * This interface is not intended to be implemented by clients.
+   */
+  interface InitializersPhaseEvent {
+
+    /**
+     * @return list of Initializers in the order of execution
+     */
+    List<Initializer> getInitializers();
+
+    boolean isStart();
+
+    boolean isEnd();
+
+  }
+
+  /**
+   * Called before and after execution of all {@link Initializers}s.
+   */
+  void onInitializersPhase(InitializersPhaseEvent event);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/MavenPhaseHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/MavenPhaseHandler.java
new file mode 100644 (file)
index 0000000..b77dc72
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.api.batch.events;
+
+
+/**
+ * @since 3.7
+ */
+public interface MavenPhaseHandler extends EventHandler {
+
+  /**
+   * This interface is not intended to be implemented by clients.
+   */
+  interface MavenPhaseEvent {
+
+    boolean isStart();
+
+    boolean isEnd();
+
+  }
+
+  /**
+   * Called before and after execution Maven phase (-Dsonar.phase=xxx).
+   */
+  void onMavenPhase(MavenPhaseEvent event);
+
+}