From cb76a1a3cad2210e5cb9fa48c034d388d8b1b45b Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Fri, 11 Mar 2011 15:14:08 +0300 Subject: [PATCH] SONAR-2272 Add EventBus --- .../src/main/java/org/sonar/batch/Batch.java | 26 +++- .../java/org/sonar/batch/ProjectBatch.java | 2 + .../batch/events/DecoratorExecutionEvent.java | 59 +++++++++ .../events/DecoratorExecutionHandler.java | 26 ++++ .../batch/events/DecoratorsPhaseEvent.java | 42 ++++++ .../batch/events/DecoratorsPhaseHandler.java | 7 + .../java/org/sonar/batch/events/EventBus.java | 66 ++++++++++ .../org/sonar/batch/events/EventHandler.java | 29 +++++ .../batch/events/SensorExecutionEvent.java | 59 +++++++++ .../batch/events/SensorExecutionHandler.java | 26 ++++ .../sonar/batch/events/SensorsPhaseEvent.java | 38 ++++++ .../batch/events/SensorsPhaseHandler.java | 7 + .../org/sonar/batch/events/SonarEvent.java | 44 +++++++ .../sonar/batch/index/MemoryOptimizer.java | 34 ++++- .../batch/phases/DecoratorsExecutor.java | 83 +++--------- .../batch/phases/PhasesTimeProfiler.java | 121 ++++++++++++++++++ .../sonar/batch/phases/SensorsExecutor.java | 35 +++-- .../org/sonar/batch/events/EventBusTest.java | 76 +++++++++++ .../batch/phases/DecoratorsExecutorTest.java | 14 +- 19 files changed, 689 insertions(+), 105 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionHandler.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseHandler.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/EventBus.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/EventHandler.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionHandler.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseHandler.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/events/SonarEvent.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/phases/PhasesTimeProfiler.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/events/EventBusTest.java diff --git a/sonar-batch/src/main/java/org/sonar/batch/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/Batch.java index c2b15adda09..d36128953ea 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/Batch.java @@ -19,6 +19,9 @@ */ package org.sonar.batch; +import java.net.URLClassLoader; +import java.util.Arrays; + import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +33,23 @@ import org.sonar.batch.bootstrap.BatchPluginRepository; import org.sonar.batch.bootstrap.BootstrapClassLoader; import org.sonar.batch.bootstrap.ExtensionDownloader; import org.sonar.batch.bootstrap.TempDirectories; -import org.sonar.batch.components.*; -import org.sonar.batch.index.*; +import org.sonar.batch.components.PastMeasuresLoader; +import org.sonar.batch.components.PastSnapshotFinder; +import org.sonar.batch.components.PastSnapshotFinderByDate; +import org.sonar.batch.components.PastSnapshotFinderByDays; +import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis; +import org.sonar.batch.components.PastSnapshotFinderByVersion; +import org.sonar.batch.events.EventBus; +import org.sonar.batch.index.DefaultIndex; +import org.sonar.batch.index.DefaultPersistenceManager; +import org.sonar.batch.index.DefaultResourcePersister; +import org.sonar.batch.index.DependencyPersister; +import org.sonar.batch.index.EventPersister; +import org.sonar.batch.index.LinkPersister; +import org.sonar.batch.index.MeasurePersister; +import org.sonar.batch.index.MemoryOptimizer; +import org.sonar.batch.index.SourcePersister; +import org.sonar.batch.index.ViolationPersister; import org.sonar.core.components.CacheMetricFinder; import org.sonar.core.components.CacheRuleFinder; import org.sonar.core.plugin.JpaPluginDao; @@ -40,9 +58,6 @@ import org.sonar.jpa.session.DatabaseSessionProvider; import org.sonar.jpa.session.DriverDatabaseConnector; import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; -import java.net.URLClassLoader; -import java.util.Arrays; - public class Batch { private static final Logger LOG = LoggerFactory.getLogger(Batch.class); @@ -89,6 +104,7 @@ public class Batch { addComponent(EventPersister.class); addComponent(LinkPersister.class); addComponent(MeasurePersister.class); + addComponent(EventBus.class); addComponent(MemoryOptimizer.class); addComponent(DefaultResourcePersister.class); addComponent(SourcePersister.class); diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java index 8614891b8b8..14406cf4ff9 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java @@ -38,6 +38,7 @@ import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.DefaultResourcePersister; import org.sonar.batch.phases.Phases; +import org.sonar.batch.phases.PhasesTimeProfiler; import org.sonar.core.components.DefaultModelFinder; import org.sonar.jpa.dao.AsyncMeasuresDao; import org.sonar.jpa.dao.AsyncMeasuresService; @@ -147,6 +148,7 @@ public class ProjectBatch { @Override protected void configure() { addComponent(Phases.class); + addComponent(PhasesTimeProfiler.class); for (Class clazz : Phases.getPhaseClasses()) { addComponent(clazz); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionEvent.java b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionEvent.java new file mode 100644 index 00000000000..d55e3eb0524 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionEvent.java @@ -0,0 +1,59 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +import org.sonar.api.batch.Decorator; + +/** + * Fired on each execution of {@link Decorator} on start and on finish. + */ +public class DecoratorExecutionEvent extends SonarEvent { + + private Decorator decorator; + private boolean start; + + public DecoratorExecutionEvent(Decorator decorator, boolean start) { + this.decorator = decorator; + this.start = start; + } + + public Decorator getDecorator() { + return decorator; + } + + public boolean isStartExecution() { + return start; + } + + public boolean isDoneExecution() { + return !start; + } + + @Override + public void dispatch(DecoratorExecutionHandler handler) { + handler.onDecoratorExecution(this); + } + + @Override + public Class getType() { + return DecoratorExecutionHandler.class; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionHandler.java b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionHandler.java new file mode 100644 index 00000000000..3cf7af0c064 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorExecutionHandler.java @@ -0,0 +1,26 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +public interface DecoratorExecutionHandler extends EventHandler { + + void onDecoratorExecution(DecoratorExecutionEvent event); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseEvent.java b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseEvent.java new file mode 100644 index 00000000000..34d92419c49 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseEvent.java @@ -0,0 +1,42 @@ +package org.sonar.batch.events; + +import org.sonar.api.batch.Decorator; + +import java.util.Collection; + +/** + * Fired before execution of {@link Decorator}s and after. + */ +public class DecoratorsPhaseEvent extends SonarEvent { + + private Collection decorators; + private boolean start; + + public DecoratorsPhaseEvent(Collection decorators, boolean start) { + this.decorators = decorators; + this.start = start; + } + + public Collection getDecorators() { + return decorators; + } + + public boolean isPhaseStart() { + return start; + } + + public boolean isPhaseDone() { + return !start; + } + + @Override + protected void dispatch(DecoratorsPhaseHandler handler) { + handler.onDecoratorsPhase(this); + } + + @Override + protected Class getType() { + return DecoratorsPhaseHandler.class; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseHandler.java b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseHandler.java new file mode 100644 index 00000000000..9c118e9c63b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/DecoratorsPhaseHandler.java @@ -0,0 +1,7 @@ +package org.sonar.batch.events; + +public interface DecoratorsPhaseHandler extends EventHandler { + + void onDecoratorsPhase(DecoratorsPhaseEvent event); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/EventBus.java b/sonar-batch/src/main/java/org/sonar/batch/events/EventBus.java new file mode 100644 index 00000000000..b9aa8cfe7ca --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/EventBus.java @@ -0,0 +1,66 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +import java.util.List; + +import com.google.common.collect.Lists; +import org.sonar.api.utils.Logs; + +/** + * Dispatches {@link SonarEvent}s. Eases decoupling by allowing objects to interact without having direct dependencies upon one another, and + * without requiring event sources to deal with maintaining handler lists. + * + * @since 2.7 + */ +public class EventBus { + + private EventHandler[] registeredHandlers; + + public EventBus(EventHandler[] handlers) { + this.registeredHandlers = handlers; + } + + /** + * Fires the given event. + */ + public void fireEvent(SonarEvent event) { + doFireEvent(event); + } + + private void doFireEvent(SonarEvent event) { + List handlers = getDispatchList(event.getType()); + Logs.INFO.debug("Dispatch event {} for {}", event, handlers); + for (EventHandler handler : handlers) { + event.dispatch(handler); + } + } + + private List getDispatchList(Class handlerType) { + List result = Lists.newArrayList(); + for (EventHandler handler : registeredHandlers) { + if (handlerType.isAssignableFrom(handler.getClass())) { + result.add(handler); + } + } + return result; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/EventHandler.java b/sonar-batch/src/main/java/org/sonar/batch/events/EventHandler.java new file mode 100644 index 00000000000..55ed106eca4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/EventHandler.java @@ -0,0 +1,29 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +/** + * Marker interface for event handlers. + * + * @since 2.7 + */ +public interface EventHandler { + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionEvent.java b/sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionEvent.java new file mode 100644 index 00000000000..50ec16564ed --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionEvent.java @@ -0,0 +1,59 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +import org.sonar.api.batch.Sensor; + +/** + * Fired on each execution of {@link Sensor} on start and on finish. + */ +public class SensorExecutionEvent extends SonarEvent { + + private Sensor sensor; + private boolean start; + + public SensorExecutionEvent(Sensor sensor, boolean start) { + this.sensor = sensor; + this.start = start; + } + + public Sensor getSensor() { + return sensor; + } + + public boolean isStartExecution() { + return start; + } + + public boolean isDoneExecution() { + return !start; + } + + @Override + public void dispatch(SensorExecutionHandler handler) { + handler.onSensorExecution(this); + } + + @Override + public Class getType() { + return SensorExecutionHandler.class; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionHandler.java b/sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionHandler.java new file mode 100644 index 00000000000..8d33e28a419 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/SensorExecutionHandler.java @@ -0,0 +1,26 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +public interface SensorExecutionHandler extends EventHandler { + + void onSensorExecution(SensorExecutionEvent event); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseEvent.java b/sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseEvent.java new file mode 100644 index 00000000000..09ef80c7965 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseEvent.java @@ -0,0 +1,38 @@ +package org.sonar.batch.events; + +import org.sonar.api.batch.Sensor; + +import java.util.Collection; + +/** + * Fired before execution of {@link Sensor}s and after. + */ +public class SensorsPhaseEvent extends SonarEvent { + + private Collection sensors; + private boolean start; + + public SensorsPhaseEvent(Collection sensors, boolean start) { + this.sensors = sensors; + this.start = start; + } + + public Collection getSensors() { + return sensors; + } + + public boolean isPhaseStart() { + return start; + } + + @Override + protected void dispatch(SensorsPhaseHandler handler) { + handler.onSensorsPhase(this); + } + + @Override + protected Class getType() { + return SensorsPhaseHandler.class; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseHandler.java b/sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseHandler.java new file mode 100644 index 00000000000..256d24e3d55 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/SensorsPhaseHandler.java @@ -0,0 +1,7 @@ +package org.sonar.batch.events; + +public interface SensorsPhaseHandler extends EventHandler { + + void onSensorsPhase(SensorsPhaseEvent event); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/events/SonarEvent.java b/sonar-batch/src/main/java/org/sonar/batch/events/SonarEvent.java new file mode 100644 index 00000000000..9a544bd6488 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/events/SonarEvent.java @@ -0,0 +1,44 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +/** + * Root of all Sonar events. + * + * @param handler type + * @since 2.7 + */ +public abstract class SonarEvent { + + protected SonarEvent() { + } + + /** + * Do not call directly - should be called only by {@link EventBus}. + * Typically should be implemented as following: handler.onEvent(this) + */ + protected abstract void dispatch(H handler); + + /** + * Returns class of associated handler. Used by {@link EventBus} to dispatch events to the correct handlers. + */ + protected abstract Class getType(); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java b/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java index aec3e771324..9c6bd043834 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java @@ -19,6 +19,9 @@ */ package org.sonar.batch.index; +import java.util.List; +import java.util.Map; + import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.slf4j.Logger; @@ -28,14 +31,17 @@ import org.sonar.api.database.model.MeasureData; import org.sonar.api.database.model.MeasureModel; import org.sonar.api.measures.Measure; import org.sonar.api.measures.PersistenceMode; - -import java.util.List; -import java.util.Map; +import org.sonar.batch.events.DecoratorExecutionEvent; +import org.sonar.batch.events.DecoratorExecutionHandler; +import org.sonar.batch.events.DecoratorsPhaseEvent; +import org.sonar.batch.events.DecoratorsPhaseHandler; +import org.sonar.batch.events.SensorExecutionEvent; +import org.sonar.batch.events.SensorExecutionHandler; /** * @since 2.7 */ -public class MemoryOptimizer { +public class MemoryOptimizer implements SensorExecutionHandler, DecoratorExecutionHandler, DecoratorsPhaseHandler { private static final Logger LOG = LoggerFactory.getLogger(MemoryOptimizer.class); @@ -94,4 +100,24 @@ public class MemoryOptimizer { boolean isTracked(Long measureId) { return dataIdByMeasureId.get(measureId) != null; } + + public void onSensorExecution(SensorExecutionEvent event) { + if (event.isDoneExecution()) { + flushMemory(); + session.commit(); + } + } + + public void onDecoratorExecution(DecoratorExecutionEvent event) { + if (event.isDoneExecution()) { + flushMemory(); + } + } + + public void onDecoratorsPhase(DecoratorsPhaseEvent event) { + if (event.isPhaseDone()) { + session.commit(); + } + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java index a8f7e3ab862..9b294e8eb90 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java @@ -19,11 +19,10 @@ */ package org.sonar.batch.phases; +import java.util.Collection; +import java.util.List; + import com.google.common.collect.Lists; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.Decorator; @@ -33,99 +32,49 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.batch.DecoratorsSelector; import org.sonar.batch.DefaultDecoratorContext; +import org.sonar.batch.events.DecoratorExecutionEvent; +import org.sonar.batch.events.DecoratorsPhaseEvent; +import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.MemoryOptimizer; - -import java.util.Collection; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; public class DecoratorsExecutor implements BatchComponent { private DecoratorsSelector decoratorsSelector; private DatabaseSession session; - private static final Logger LOG = LoggerFactory.getLogger(DecoratorsExecutor.class); private DefaultIndex index; - private MemoryOptimizer memoryOptimizer; + private EventBus eventBus; - public DecoratorsExecutor(BatchExtensionDictionnary extensionDictionnary, DefaultIndex index, DatabaseSession session, - MemoryOptimizer memoryOptimizer) { + public DecoratorsExecutor(BatchExtensionDictionnary extensionDictionnary, DefaultIndex index, DatabaseSession session, EventBus eventBus) { this.decoratorsSelector = new DecoratorsSelector(extensionDictionnary); this.session = session; this.index = index; - this.memoryOptimizer = memoryOptimizer; + this.eventBus = eventBus; } - public void execute(Project project) { - LoggerFactory.getLogger(DecoratorsExecutor.class).info("Execute decorators..."); Collection decorators = decoratorsSelector.select(project); - - if (LOG.isDebugEnabled()) { - LOG.debug("Decorators: {}", StringUtils.join(decorators, " -> ")); - } - - DecoratorsProfiler profiler = new DecoratorsProfiler(decorators); - decorateResource(project, decorators, true, profiler); - session.commit(); - profiler.log(); + eventBus.fireEvent(new DecoratorsPhaseEvent(decorators, true)); + decorateResource(project, decorators, true); + eventBus.fireEvent(new DecoratorsPhaseEvent(decorators, false)); } - private DecoratorContext decorateResource(Resource resource, Collection decorators, boolean executeDecorators, DecoratorsProfiler profiler) { + private DecoratorContext decorateResource(Resource resource, Collection decorators, boolean executeDecorators) { List childrenContexts = Lists.newArrayList(); for (Resource child : index.getChildren(resource)) { boolean isModule = (child instanceof Project); - DefaultDecoratorContext childContext = (DefaultDecoratorContext) decorateResource(child, decorators, !isModule, profiler); + DefaultDecoratorContext childContext = (DefaultDecoratorContext) decorateResource(child, decorators, !isModule); childrenContexts.add(childContext.setReadOnly(true)); } DefaultDecoratorContext context = new DefaultDecoratorContext(resource, index, childrenContexts, session); if (executeDecorators) { for (Decorator decorator : decorators) { - profiler.start(decorator); + eventBus.fireEvent(new DecoratorExecutionEvent(decorator, true)); decorator.decorate(resource, context); - memoryOptimizer.flushMemory(); - profiler.stop(); + eventBus.fireEvent(new DecoratorExecutionEvent(decorator, false)); } } return context; } - - static class DecoratorsProfiler { - Collection decorators; - Map durations = new IdentityHashMap(); - long startTime; - Decorator currentDecorator; - - DecoratorsProfiler(Collection decorators) { - this.decorators = decorators; - for (Decorator decorator : decorators) { - durations.put(decorator, 0L); - } - } - - void start(Decorator decorator) { - this.startTime = System.currentTimeMillis(); - this.currentDecorator = decorator; - } - - void stop() { - Long cumulatedDuration = durations.get(currentDecorator); - durations.put(currentDecorator, cumulatedDuration + (System.currentTimeMillis() - startTime)); - } - - void log() { - LOG.debug(getMessage()); - } - - String getMessage() { - StringBuilder sb = new StringBuilder("Decorator time:").append(SystemUtils.LINE_SEPARATOR); - for (Decorator decorator : decorators) { - sb.append("\t").append(decorator.toString()).append(": ").append(durations.get(decorator)).append("ms").append(SystemUtils.LINE_SEPARATOR); - } - return sb.toString(); - } - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhasesTimeProfiler.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhasesTimeProfiler.java new file mode 100644 index 00000000000..07ceaba617d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PhasesTimeProfiler.java @@ -0,0 +1,121 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.phases; + +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.SystemUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Decorator; +import org.sonar.api.utils.TimeProfiler; +import org.sonar.batch.events.DecoratorExecutionEvent; +import org.sonar.batch.events.DecoratorExecutionHandler; +import org.sonar.batch.events.DecoratorsPhaseEvent; +import org.sonar.batch.events.DecoratorsPhaseHandler; +import org.sonar.batch.events.SensorExecutionEvent; +import org.sonar.batch.events.SensorExecutionHandler; +import org.sonar.batch.events.SensorsPhaseEvent; +import org.sonar.batch.events.SensorsPhaseHandler; + +public class PhasesTimeProfiler implements SensorExecutionHandler, DecoratorExecutionHandler, DecoratorsPhaseHandler, SensorsPhaseHandler { + + private static final Logger LOG = LoggerFactory.getLogger(PhasesTimeProfiler.class); + + private TimeProfiler profiler = new TimeProfiler(LOG); + private DecoratorsProfiler decoratorsProfiler = new DecoratorsProfiler(); + + public void onSensorsPhase(SensorsPhaseEvent event) { + if (event.isPhaseStart()) { + LOG.debug("Sensors : {}", StringUtils.join(event.getSensors(), " -> ")); + } + } + + public void onSensorExecution(SensorExecutionEvent event) { + if (event.isStartExecution()) { + profiler.start("Sensor " + event.getSensor()); + } else { + profiler.stop(); + } + } + + public void onDecoratorExecution(DecoratorExecutionEvent event) { + if (event.isStartExecution()) { + decoratorsProfiler.start(event.getDecorator()); + } else { + decoratorsProfiler.stop(); + } + } + + public void onDecoratorsPhase(DecoratorsPhaseEvent event) { + if (event.isPhaseStart()) { + LOG.info("Execute decorators..."); + if (LOG.isDebugEnabled()) { + LOG.debug("Decorators: {}", StringUtils.join(event.getDecorators(), " -> ")); + } + } else { + decoratorsProfiler.log(); + } + } + + static class DecoratorsProfiler { + List decorators = Lists.newArrayList(); + Map durations = new IdentityHashMap(); + long startTime; + Decorator currentDecorator; + + DecoratorsProfiler() { + } + + void start(Decorator decorator) { + this.startTime = System.currentTimeMillis(); + this.currentDecorator = decorator; + } + + void stop() { + final Long cumulatedDuration; + if (durations.containsKey(currentDecorator)) { + cumulatedDuration = durations.get(currentDecorator); + } else { + decorators.add(currentDecorator); + cumulatedDuration = 0L; + } + durations.put(currentDecorator, cumulatedDuration + (System.currentTimeMillis() - startTime)); + } + + void log() { + LOG.debug(getMessage()); + } + + String getMessage() { + StringBuilder sb = new StringBuilder("Decorator time:").append(SystemUtils.LINE_SEPARATOR); + for (Decorator decorator : decorators) { + sb.append("\t").append(decorator.toString()).append(": ").append(durations.get(decorator)).append("ms") + .append(SystemUtils.LINE_SEPARATOR); + } + return sb.toString(); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/SensorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/SensorsExecutor.java index e78a56f25c7..ba767cd0d40 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/SensorsExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/SensorsExecutor.java @@ -19,7 +19,8 @@ */ package org.sonar.batch.phases; -import org.apache.commons.lang.StringUtils; +import java.util.Collection; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; @@ -28,51 +29,45 @@ import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; -import org.sonar.api.database.DatabaseSession; import org.sonar.api.resources.Project; import org.sonar.api.utils.TimeProfiler; import org.sonar.batch.MavenPluginExecutor; -import org.sonar.batch.index.MemoryOptimizer; - -import java.util.Collection; +import org.sonar.batch.events.EventBus; +import org.sonar.batch.events.SensorExecutionEvent; +import org.sonar.batch.events.SensorsPhaseEvent; public class SensorsExecutor implements BatchComponent { - private static final Logger logger = LoggerFactory.getLogger(SensorsExecutor.class); + private static final Logger LOG = LoggerFactory.getLogger(SensorsExecutor.class); private Collection sensors; - private DatabaseSession session; private MavenPluginExecutor mavenExecutor; - private MemoryOptimizer memoryOptimizer; + private EventBus eventBus; - public SensorsExecutor(BatchExtensionDictionnary selector, Project project, DatabaseSession session, MavenPluginExecutor mavenExecutor, - MemoryOptimizer memoryOptimizer) { + public SensorsExecutor(BatchExtensionDictionnary selector, Project project, MavenPluginExecutor mavenExecutor, EventBus eventBus) { this.sensors = selector.select(Sensor.class, project, true); - this.session = session; this.mavenExecutor = mavenExecutor; - this.memoryOptimizer = memoryOptimizer; + this.eventBus = eventBus; } public void execute(Project project, SensorContext context) { - if (logger.isDebugEnabled()) { - logger.debug("Sensors : {}", StringUtils.join(sensors, " -> ")); - } + eventBus.fireEvent(new SensorsPhaseEvent(sensors, true)); for (Sensor sensor : sensors) { executeMavenPlugin(project, sensor); - TimeProfiler profiler = new TimeProfiler(logger).start("Sensor " + sensor); + eventBus.fireEvent(new SensorExecutionEvent(sensor, true)); sensor.analyse(project, context); - memoryOptimizer.flushMemory(); - session.commit(); - profiler.stop(); + eventBus.fireEvent(new SensorExecutionEvent(sensor, false)); } + + eventBus.fireEvent(new SensorsPhaseEvent(sensors, false)); } private void executeMavenPlugin(Project project, Sensor sensor) { if (sensor instanceof DependsUponMavenPlugin) { MavenPluginHandler handler = ((DependsUponMavenPlugin) sensor).getMavenPluginHandler(project); if (handler != null) { - TimeProfiler profiler = new TimeProfiler(logger).start("Execute maven plugin " + handler.getArtifactId()); + TimeProfiler profiler = new TimeProfiler(LOG).start("Execute maven plugin " + handler.getArtifactId()); mavenExecutor.execute(project, handler); profiler.stop(); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/events/EventBusTest.java b/sonar-batch/src/test/java/org/sonar/batch/events/EventBusTest.java new file mode 100644 index 00000000000..5817a80a336 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/events/EventBusTest.java @@ -0,0 +1,76 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.events; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Test; + +public class EventBusTest { + + @Test + public void shouldNotifyAboutEvent() { + FirstHandler firstHandler = mock(FirstHandler.class); + SecondHandler secondHandler = mock(SecondHandler.class); + EventBus eventBus = new EventBus(new EventHandler[] { firstHandler, secondHandler }); + + FirstEvent firstEvent = new FirstEvent(); + eventBus.fireEvent(firstEvent); + SecondEvent secondEvent = new SecondEvent(); + eventBus.fireEvent(secondEvent); + + verify(firstHandler).onEvent(firstEvent); + verify(secondHandler).onEvent(secondEvent); + } + + interface FirstHandler extends EventHandler { + void onEvent(FirstEvent event); + } + + static class FirstEvent extends SonarEvent { + @Override + protected void dispatch(FirstHandler handler) { + handler.onEvent(this); + } + + @Override + public Class getType() { + return FirstHandler.class; + } + } + + interface SecondHandler extends EventHandler { + void onEvent(SecondEvent event); + } + + static class SecondEvent extends SonarEvent { + @Override + protected void dispatch(SecondHandler handler) { + handler.onEvent(this); + } + + @Override + public Class getType() { + return SecondHandler.class; + } + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java index 05cc6574703..396ce2b4e36 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java @@ -19,27 +19,23 @@ */ package org.sonar.batch.phases; +import static org.hamcrest.number.OrderingComparisons.greaterThanOrEqualTo; +import static org.hamcrest.number.OrderingComparisons.lessThan; +import static org.junit.Assert.assertThat; + import org.junit.Test; import org.sonar.api.batch.Decorator; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; -import java.util.Arrays; -import java.util.List; - -import static org.hamcrest.number.OrderingComparisons.greaterThanOrEqualTo; -import static org.hamcrest.number.OrderingComparisons.lessThan; -import static org.junit.Assert.assertThat; - public class DecoratorsExecutorTest { @Test public void shouldProfileExecutionTime() { Decorator1 decorator1 = new Decorator1(); Decorator2 decorator2 = new Decorator2(); - List decorators = Arrays.asList(decorator1, decorator2); - DecoratorsExecutor.DecoratorsProfiler profiler = new DecoratorsExecutor.DecoratorsProfiler(decorators); + PhasesTimeProfiler.DecoratorsProfiler profiler = new PhasesTimeProfiler.DecoratorsProfiler(); profiler.start(decorator1); profiler.stop(); -- 2.39.5