From 4c0b66d797afc6ecc322caaaa84c4e6542443d18 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Wed, 12 Jun 2013 09:47:28 +0200 Subject: [PATCH] SONAR-4384 Add missing phases to Sonar profiling --- .../sonar/batch/events/BatchStepEvent.java | 62 ++++++++ .../sonar/batch/events/BatchStepHandler.java | 47 ++++++ .../phases/InitializerExecutionEvent.java | 49 +++++++ .../batch/phases/InitializersExecutor.java | 13 +- .../batch/phases/InitializersPhaseEvent.java | 51 +++++++ .../sonar/batch/phases/MavenPhaseEvent.java | 41 ++++++ .../org/sonar/batch/phases/PhaseExecutor.java | 91 ++++++++---- .../profiling/AbstractTimeProfiling.java | 28 +++- .../batch/profiling/ModuleProfiling.java | 38 +++-- .../sonar/batch/profiling/PhaseProfiling.java | 11 +- .../profiling/PhasesSumUpTimeProfiler.java | 87 ++++++++++-- .../java/org/sonar/batch/scan/JsonReport.java | 58 ++++---- .../PhasesSumUpTimeProfilerTest.java | 134 ++++++++++++++++++ .../org/sonar/batch/scan/JsonReportTest.java | 61 ++++---- .../events/InitializerExecutionHandler.java | 47 ++++++ .../events/InitializersPhaseHandler.java | 52 +++++++ .../api/batch/events/MavenPhaseHandler.java | 44 ++++++ 17 files changed, 793 insertions(+), 121 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/BatchStepEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/BatchStepHandler.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/phases/InitializerExecutionEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/phases/InitializersPhaseEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseEvent.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializerExecutionHandler.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializersPhaseHandler.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/events/MavenPhaseHandler.java 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 index 00000000000..30036917e3b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/BatchStepEvent.java @@ -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 + 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 index 00000000000..2f4a39548c9 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/BatchStepHandler.java @@ -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 index 00000000000..e85969d94b5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializerExecutionEvent.java @@ -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 + 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; + } + +} 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 index e3d391659b2..e57f5908a9e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersExecutor.java @@ -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 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 index 00000000000..a3d48fc2654 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/InitializersPhaseEvent.java @@ -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 + implements org.sonar.api.batch.events.InitializersPhaseHandler.InitializersPhaseEvent { + + private final List initializers; + + InitializersPhaseEvent(List initializers, boolean start) { + super(start); + this.initializers = initializers; + } + + public List 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 index 00000000000..1552e50dc7d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseEvent.java @@ -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 + 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; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java index b690f19d43e..8b8dd93c7a9 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java @@ -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 getPhaseClasses() { - return Lists.newArrayList(DecoratorsExecutor.class, MavenPhaseExecutor.class, MavenPluginsConfigurator.class, - PostJobsExecutor.class, SensorsExecutor.class, - InitializersExecutor.class, ProjectInitializer.class, UpdateStatusJob.class); + return Lists. 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)); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/profiling/AbstractTimeProfiling.java b/sonar-batch/src/main/java/org/sonar/batch/profiling/AbstractTimeProfiling.java index a610520ef5f..badcec28122 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/profiling/AbstractTimeProfiling.java +++ b/sonar-batch/src/main/java/org/sonar/batch/profiling/AbstractTimeProfiling.java @@ -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 List sortByDescendingTotalTime(Collection unsorted) { - List result = new ArrayList(unsorted.size()); - result.addAll(unsorted); - Collections.sort(result, new Comparator() { + static Map sortByDescendingTotalTime(Map unsorted) { + List> entries = + new ArrayList>(unsorted.entrySet()); + Collections.sort(entries, new Comparator>() { @Override - public int compare(G o1, G o2) { - return Long.valueOf(o2.totalTime()).compareTo(o1.totalTime()); + public int compare(Map.Entry o1, Map.Entry o2) { + return Long.valueOf(o2.getValue().totalTime()).compareTo(o1.getValue().totalTime()); } }); - return result; + Map sortedMap = new LinkedHashMap(); + for (Map.Entry entry : entries) { + sortedMap.put(entry.getKey(), entry.getValue()); + } + return sortedMap; } static List truncate(Collection 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); + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/profiling/ModuleProfiling.java b/sonar-batch/src/main/java/org/sonar/batch/profiling/ModuleProfiling.java index e1b48fcba69..e7ba4104cb0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/profiling/ModuleProfiling.java +++ b/sonar-batch/src/main/java/org/sonar/batch/profiling/ModuleProfiling.java @@ -19,20 +19,22 @@ */ 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 profilingPerPhase = new HashMap(); + private Map profilingPerBatchStep = new LinkedHashMap(); 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 categories = Maps.newLinkedHashMap(); + categories.putAll(profilingPerPhase); + categories.putAll(profilingPerBatchStep); + + for (Map.Entry 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 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()); + } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/profiling/PhaseProfiling.java b/sonar-batch/src/main/java/org/sonar/batch/profiling/PhaseProfiling.java index 8bb5bcbdacd..45ba18a17bb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/profiling/PhaseProfiling.java +++ b/sonar-batch/src/main/java/org/sonar/batch/profiling/PhaseProfiling.java @@ -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); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java b/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java index 6137b79875e..6aebf4f1b86 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java +++ b/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java @@ -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 modulesProfilings = new ArrayList(); + private Map modulesProfilings = new HashMap(); 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 decorators = Lists.newArrayList(); Map durations = new IdentityHashMap(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/JsonReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/JsonReport.java index 33b2097ee3f..49bfcc5fa02 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/JsonReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/JsonReport.java @@ -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(); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java b/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java index b7a56ce60a4..9c504dbe35a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java @@ -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 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; } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/JsonReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/JsonReportTest.java index 3f92ad420f3..51ad93918b4 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/JsonReportTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/JsonReportTest.java @@ -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.newArrayList(issue)); + when(jsonReport.getIssues()).thenReturn(Lists. 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.newArrayList(issue)); + when(jsonReport.getIssues()).thenReturn(Lists. 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.emptyList()); + when(jsonReport.getIssues()).thenReturn(Collections. 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.emptyList()); + when(jsonReport.getIssues()).thenReturn(Collections. 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 index 00000000000..951d0536656 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializerExecutionHandler.java @@ -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 index 00000000000..7f57647df38 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/InitializersPhaseHandler.java @@ -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 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 index 00000000000..b77dc72f5c5 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/events/MavenPhaseHandler.java @@ -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); + +} -- 2.39.5