diff options
author | Simon Brandhof <simon.brandhof@gmail.com> | 2013-03-14 23:17:22 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2013-03-14 23:17:22 +0100 |
commit | c2eab2217274ce151f2a330348d649997c278d03 (patch) | |
tree | 12fc44f7f1aba23e1a280f57d7b2e4c12a16f4a4 | |
parent | 3f4f0ba6cd0380e1f918aa3d85eb0e1271775905 (diff) | |
download | sonarqube-c2eab2217274ce151f2a330348d649997c278d03.tar.gz sonarqube-c2eab2217274ce151f2a330348d649997c278d03.zip |
Improve exception handling of pico container lifecycle
12 files changed, 343 insertions, 261 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java index eb808e74ed6..d8491e18758 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java @@ -33,6 +33,8 @@ import org.sonar.batch.components.PastSnapshotFinderByDays; import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis; import org.sonar.batch.components.PastSnapshotFinderByPreviousVersion; import org.sonar.batch.components.PastSnapshotFinderByVersion; +import org.sonar.batch.scan.ScanTask; +import org.sonar.batch.tasks.ListTask; import org.sonar.core.config.Logback; import org.sonar.core.i18n.I18nManager; import org.sonar.core.i18n.RuleI18nManager; @@ -60,6 +62,12 @@ public class BootstrapContainer extends ComponentContainer { super(); } + public static BootstrapContainer create(List objects) { + BootstrapContainer container = new BootstrapContainer(); + container.add(objects); + return container; + } + @Override protected void doBeforeStart() { addBootstrapComponents(); @@ -69,36 +77,36 @@ public class BootstrapContainer extends ComponentContainer { private void addBootstrapComponents() { add( - new PropertiesConfiguration(), - BootstrapSettings.class, - PluginDownloader.class, - BatchPluginRepository.class, - BatchSettings.class, - ServerClient.class, - ExtensionInstaller.class, - Logback.class, - ServerMetadata.class, - org.sonar.batch.ServerMetadata.class, - TempDirectories.class, - HttpDownloader.class, - UriReader.class, - new FileCacheProvider() + new PropertiesConfiguration(), + BootstrapSettings.class, + PluginDownloader.class, + BatchPluginRepository.class, + BatchSettings.class, + ServerClient.class, + ExtensionInstaller.class, + Logback.class, + ServerMetadata.class, + org.sonar.batch.ServerMetadata.class, + TempDirectories.class, + HttpDownloader.class, + UriReader.class, + new FileCacheProvider() ); } private void addDatabaseComponents() { add( - DryRunDatabase.class, - JdbcDriverHolder.class, - BatchDatabase.class, - MyBatis.class, - DatabaseVersion.class, - //TODO check that it still works (see @Freddy) - DatabaseCompatibility.class, - DefaultDatabaseConnector.class, - JpaDatabaseSession.class, - BatchDatabaseSessionFactory.class, - DaoUtils.getDaoClasses() + DryRunDatabase.class, + JdbcDriverHolder.class, + BatchDatabase.class, + MyBatis.class, + DatabaseVersion.class, + //TODO check that it still works (see @Freddy) + DatabaseCompatibility.class, + DefaultDatabaseConnector.class, + JpaDatabaseSession.class, + BatchDatabaseSessionFactory.class, + DaoUtils.getDaoClasses() ); } @@ -107,44 +115,43 @@ public class BootstrapContainer extends ComponentContainer { */ private void addCoreComponents() { add( - EmailSettings.class, - I18nManager.class, - RuleI18nManager.class, - MeasuresDao.class, - RulesDao.class, - ProfilesDao.class, - CacheRuleFinder.class, - CacheMetricFinder.class, - DefaultUserFinder.class, - SemaphoreUpdater.class, - SemaphoresImpl.class, - PastSnapshotFinderByDate.class, - PastSnapshotFinderByDays.class, - PastSnapshotFinderByPreviousAnalysis.class, - PastSnapshotFinderByVersion.class, - PastSnapshotFinderByPreviousVersion.class, - PastMeasuresLoader.class, - PastSnapshotFinder.class, - DefaultModelFinder.class + EmailSettings.class, + I18nManager.class, + RuleI18nManager.class, + MeasuresDao.class, + RulesDao.class, + ProfilesDao.class, + CacheRuleFinder.class, + CacheMetricFinder.class, + DefaultUserFinder.class, + SemaphoreUpdater.class, + SemaphoresImpl.class, + PastSnapshotFinderByDate.class, + PastSnapshotFinderByDays.class, + PastSnapshotFinderByPreviousAnalysis.class, + PastSnapshotFinderByVersion.class, + PastSnapshotFinderByPreviousVersion.class, + PastMeasuresLoader.class, + PastSnapshotFinder.class, + DefaultModelFinder.class ); } @Override protected void doAfterStart() { installPlugins(); + executeTask(); } private void installPlugins() { - for (Map.Entry<PluginMetadata, Plugin> entry : get(BatchPluginRepository.class).getPluginsByMetadata().entrySet()) { + for (Map.Entry<PluginMetadata, Plugin> entry : getComponentByType(BatchPluginRepository.class).getPluginsByMetadata().entrySet()) { PluginMetadata metadata = entry.getKey(); Plugin plugin = entry.getValue(); addExtension(metadata, plugin); } } - public static BootstrapContainer create(List objects) { - BootstrapContainer container = new BootstrapContainer(); - container.add(objects); - return container; + void executeTask() { + new TaskContainer(this).execute(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java index bc50ede3ea2..d1772e1497c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java @@ -27,6 +27,8 @@ import org.sonar.api.task.Task; import org.sonar.api.task.TaskDefinition; import org.sonar.api.task.TaskExtension; import org.sonar.api.utils.SonarException; +import org.sonar.batch.scan.ScanTask; +import org.sonar.batch.tasks.ListTask; import org.sonar.batch.tasks.Tasks; public class TaskContainer extends ComponentContainer { @@ -37,30 +39,20 @@ public class TaskContainer extends ComponentContainer { @Override protected void doBeforeStart() { + installCoreTasks(); installTaskExtensions(); installComponentsUsingTaskExtensions(); } - public void execute() { - startComponents(); - - // default value is declared in CorePlugin - String taskKey = get(Settings.class).getString(CoreProperties.TASK); - - TaskDefinition def = get(Tasks.class).definition(taskKey); - if (def == null) { - throw new SonarException("Task " + taskKey + " does not exist."); - } - Task task = get(def.taskClass()); - if (task != null) { - task.execute(); - } else { - throw new IllegalStateException("Task " + taskKey + " is badly defined."); - } + private void installCoreTasks() { + add( + ScanTask.DEFINITION, ScanTask.class, + ListTask.DEFINITION, ListTask.class + ); } private void installTaskExtensions() { - get(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() { + getComponentByType(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() { public boolean accept(Object extension) { return ExtensionUtils.isType(extension, TaskExtension.class); } @@ -70,4 +62,21 @@ public class TaskContainer extends ComponentContainer { private void installComponentsUsingTaskExtensions() { add(ResourceTypes.class, MetricProvider.class, Tasks.class); } + + @Override + public void doAfterStart() { + // default value is declared in CorePlugin + String taskKey = getComponentByType(Settings.class).getString(CoreProperties.TASK); + + TaskDefinition def = getComponentByType(Tasks.class).definition(taskKey); + if (def == null) { + throw new SonarException("Task " + taskKey + " does not exist"); + } + Task task = getComponentByType(def.taskClass()); + if (task != null) { + task.execute(); + } else { + throw new IllegalStateException("Task " + taskKey + " is badly defined"); + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java index fd2ed69ac6c..d7459b0e7a7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java @@ -21,14 +21,9 @@ package org.sonar.batch.bootstrapper; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import org.slf4j.LoggerFactory; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.batch.bootstrap.BootstrapContainer; import org.sonar.batch.bootstrap.BootstrapProperties; -import org.sonar.batch.bootstrap.TaskContainer; -import org.sonar.batch.scan.ScanTask; -import org.sonar.batch.tasks.ListTask; -import org.sonar.core.PicoUtils; import java.util.Collections; import java.util.List; @@ -81,37 +76,14 @@ public final class Batch { } private void startBatch() { - BootstrapContainer bootstrapContainer = null; - try { - List all = Lists.newArrayList(components); - all.add(new BootstrapProperties(bootstrapProperties)); - if (projectReactor != null) { - all.add(projectReactor); - } - - bootstrapContainer = BootstrapContainer.create(all); - bootstrapContainer.startComponents(); - - TaskContainer taskContainer = new TaskContainer(bootstrapContainer); - taskContainer.add( - ScanTask.DEFINITION, ScanTask.class, - ListTask.DEFINITION, ListTask.class - ); - - taskContainer.execute(); - - } catch (RuntimeException e) { - PicoUtils.propagateStartupException(e); - } finally { - try { - if (bootstrapContainer != null) { - bootstrapContainer.stopComponents(); - } - } catch (Exception e) { - // never throw exceptions in a finally block - LoggerFactory.getLogger(Batch.class).error("Error while stopping batch", e); - } + List all = Lists.newArrayList(components); + all.add(new BootstrapProperties(bootstrapProperties)); + if (projectReactor != null) { + all.add(projectReactor); } + + BootstrapContainer bootstrapContainer = BootstrapContainer.create(all); + bootstrapContainer.execute(); } public static Builder builder() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index b85b7e75bb4..0e001d1c3b7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -71,54 +71,54 @@ public class ModuleScanContainer extends ComponentContainer { } private void addCoreComponents() { - ProjectDefinition moduleDefinition = get(ProjectTree.class).getProjectDefinition(module); + ProjectDefinition moduleDefinition = getComponentByType(ProjectTree.class).getProjectDefinition(module); add( - moduleDefinition, - module.getConfiguration(), - module, - ModuleSettings.class); + moduleDefinition, + module.getConfiguration(), + module, + ModuleSettings.class); // hack to initialize commons-configuration before ExtensionProviders - get(ModuleSettings.class); + getComponentByType(ModuleSettings.class); add( - EventBus.class, - Phases.class, - PhasesTimeProfiler.class, - UnsupportedProperties.class, - Phases.getPhaseClasses(), - moduleDefinition.getContainerExtensions(), - - // TODO move outside project, but not possible yet because of dependency of project settings (cf plsql) - Languages.class, - - // file system - PathResolver.class, - FileExclusions.class, - LanguageFilters.class, - ExclusionFilters.class, - DefaultProjectClasspath.class, - new ModuleFileSystemProvider(), - DeprecatedFileSystemAdapter.class, - FileSystemLogger.class, - - // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) - get(ResourcePersister.class).getSnapshot(module), - - TimeMachineConfiguration.class, - org.sonar.api.database.daos.MeasuresDao.class, - DefaultSensorContext.class, - BatchExtensionDictionnary.class, - DefaultTimeMachine.class, - ViolationFilters.class, - ResourceFilters.class, - DefaultProfileLoader.class, - DryRunExporter.class, - new ProfileProvider()); + EventBus.class, + Phases.class, + PhasesTimeProfiler.class, + UnsupportedProperties.class, + Phases.getPhaseClasses(), + moduleDefinition.getContainerExtensions(), + + // TODO move outside project, but not possible yet because of dependency of project settings (cf plsql) + Languages.class, + + // file system + PathResolver.class, + FileExclusions.class, + LanguageFilters.class, + ExclusionFilters.class, + DefaultProjectClasspath.class, + new ModuleFileSystemProvider(), + DeprecatedFileSystemAdapter.class, + FileSystemLogger.class, + + // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) + getComponentByType(ResourcePersister.class).getSnapshot(module), + + TimeMachineConfiguration.class, + org.sonar.api.database.daos.MeasuresDao.class, + DefaultSensorContext.class, + BatchExtensionDictionnary.class, + DefaultTimeMachine.class, + ViolationFilters.class, + ResourceFilters.class, + DefaultProfileLoader.class, + DryRunExporter.class, + new ProfileProvider()); } private void addExtensions() { - ExtensionInstaller installer = get(ExtensionInstaller.class); + ExtensionInstaller installer = getComponentByType(ExtensionInstaller.class); installer.install(this, new ExtensionInstaller.ComponentFilter() { public boolean accept(Object extension) { if (ExtensionUtils.isType(extension, BatchExtension.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) { @@ -134,14 +134,13 @@ public class ModuleScanContainer extends ComponentContainer { @Override protected void doAfterStart() { - DefaultIndex index = get(DefaultIndex.class); + DefaultIndex index = getComponentByType(DefaultIndex.class); index.setCurrentProject(module, - get(ResourceFilters.class), - get(ViolationFilters.class), - get(RulesProfile.class)); + getComponentByType(ResourceFilters.class), + getComponentByType(ViolationFilters.class), + getComponentByType(RulesProfile.class)); - get(Phases.class).execute(module); + getComponentByType(Phases.class).execute(module); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 74eaedaab4a..f57bbb2e085 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -63,52 +63,52 @@ public class ProjectScanContainer extends ComponentContainer { private void addBatchComponents() { add( - DefaultResourceCreationLock.class, - DefaultPersistenceManager.class, - DependencyPersister.class, - EventPersister.class, - LinkPersister.class, - MeasurePersister.class, - MemoryOptimizer.class, - DefaultResourcePermissions.class, - DefaultResourcePersister.class, - SourcePersister.class, - DefaultNotificationManager.class, - MetricProvider.class, - ProjectExclusions.class, - ProjectReactorReady.class, - ProjectTree.class, - ProjectConfigurator.class, - DefaultIndex.class, - DefaultFileLinesContextFactory.class, - ProjectLock.class, - LastSnapshots.class, - ScanGraph.create(), - TestPlanBuilder.class, - TestableBuilder.class, - ScanPerspectives.class, - ScanGraphStore.class + DefaultResourceCreationLock.class, + DefaultPersistenceManager.class, + DependencyPersister.class, + EventPersister.class, + LinkPersister.class, + MeasurePersister.class, + MemoryOptimizer.class, + DefaultResourcePermissions.class, + DefaultResourcePersister.class, + SourcePersister.class, + DefaultNotificationManager.class, + MetricProvider.class, + ProjectExclusions.class, + ProjectReactorReady.class, + ProjectTree.class, + ProjectConfigurator.class, + DefaultIndex.class, + DefaultFileLinesContextFactory.class, + ProjectLock.class, + LastSnapshots.class, + ScanGraph.create(), + TestPlanBuilder.class, + TestableBuilder.class, + ScanPerspectives.class, + ScanGraphStore.class ); } private void fixMavenExecutor() { - if (get(MavenPluginExecutor.class) == null) { + if (getComponentByType(MavenPluginExecutor.class) == null) { add(FakeMavenPluginExecutor.class); } } private void addBatchExtensions() { - get(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() { + getComponentByType(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() { public boolean accept(Object extension) { return ExtensionUtils.isType(extension, BatchExtension.class) - && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH); + && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH); } }); } @Override protected void doAfterStart() { - ProjectTree tree = get(ProjectTree.class); + ProjectTree tree = getComponentByType(ProjectTree.class); scanRecursively(tree.getRootProject()); } @@ -120,13 +120,7 @@ public class ProjectScanContainer extends ComponentContainer { } private void scan(Project module) { - ModuleScanContainer moduleContainer = new ModuleScanContainer(this, module); - try { - moduleContainer.startComponents(); - } finally { - moduleContainer.stopComponents(); - removeChild(); - } + new ModuleScanContainer(this, module).execute(); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapContainerTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapContainerTest.java index f573be1d0fa..db3a4d21bfe 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapContainerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapContainerTest.java @@ -33,7 +33,9 @@ import java.util.Collections; import java.util.List; import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; public class BootstrapContainerTest { @@ -42,8 +44,8 @@ public class BootstrapContainerTest { BootstrapContainer container = BootstrapContainer.create(Collections.emptyList()); container.doBeforeStart(); - assertThat(container.get(Logback.class)).isNotNull(); - assertThat(container.get(TempDirectories.class)).isNotNull(); + assertThat(container.getComponentByType(Logback.class)).isNotNull(); + assertThat(container.getComponentByType(TempDirectories.class)).isNotNull(); } @Test @@ -51,8 +53,8 @@ public class BootstrapContainerTest { BootstrapContainer container = BootstrapContainer.create(Lists.newArrayList(Foo.class, new Bar())); container.doBeforeStart(); - assertThat(container.get(Foo.class)).isNotNull(); - assertThat(container.get(Bar.class)).isNotNull(); + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNotNull(); } @Test @@ -64,7 +66,8 @@ public class BootstrapContainerTest { metadata, plugin )); - BootstrapContainer container = BootstrapContainer.create(Lists.newArrayList(pluginRepository)); + BootstrapContainer container = spy(BootstrapContainer.create(Lists.newArrayList(pluginRepository))); + doNothing().when(container).executeTask(); container.doAfterStart(); assertThat(container.getComponentsByType(Plugin.class)).containsOnly(plugin); diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java index 23e2a311bf4..c7fbaa32855 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java @@ -47,11 +47,11 @@ public class ExtensionInstallerTest { Map<PluginMetadata, Plugin> newPlugin(final Object... extensions) { Map<PluginMetadata, Plugin> result = Maps.newHashMap(); result.put(metadata, - new SonarPlugin() { - public List<?> getExtensions() { - return Arrays.asList(extensions); - } + new SonarPlugin() { + public List<?> getExtensions() { + return Arrays.asList(extensions); } + } ); return result; } @@ -64,8 +64,8 @@ public class ExtensionInstallerTest { ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings()); installer.install(container, new FooMatcher()); - assertThat(container.get(Foo.class)).isNotNull(); - assertThat(container.get(Bar.class)).isNull(); + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNull(); } @Test @@ -77,8 +77,8 @@ public class ExtensionInstallerTest { installer.install(container, new FooMatcher()); - assertThat(container.get(Foo.class)).isNotNull(); - assertThat(container.get(Bar.class)).isNull(); + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNull(); } @Test @@ -90,8 +90,8 @@ public class ExtensionInstallerTest { installer.install(container, new TrueMatcher()); - assertThat(container.get(Foo.class)).isNotNull(); - assertThat(container.get(Bar.class)).isNotNull(); + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNotNull(); } @Test @@ -104,10 +104,10 @@ public class ExtensionInstallerTest { installer.install(container, new TrueMatcher()); - assertThat(container.get(MavenExtension.class)).isNull(); - assertThat(container.get(AntExtension.class)).isNotNull(); - assertThat(container.get(Foo.class)).isNotNull(); - assertThat(container.get(Bar.class)).isNotNull(); + assertThat(container.getComponentByType(MavenExtension.class)).isNull(); + assertThat(container.getComponentByType(AntExtension.class)).isNotNull(); + assertThat(container.getComponentByType(Foo.class)).isNotNull(); + assertThat(container.getComponentByType(Bar.class)).isNotNull(); } private static class FooMatcher implements ExtensionInstaller.ComponentFilter { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java index ee15a324f9f..dd4b5719cd5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java @@ -27,12 +27,15 @@ import org.picocontainer.MutablePicoContainer; import org.picocontainer.behaviors.OptInCaching; import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; import org.picocontainer.monitors.NullComponentMonitor; +import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.config.PropertyDefinitions; import javax.annotation.Nullable; +import java.util.List; + /** * @since 2.12 */ @@ -65,21 +68,42 @@ public class ComponentContainer implements BatchComponent, ServerComponent { addSingleton(this); } + public void execute() { + boolean threw = true; + try { + startComponents(); + threw = false; + } finally { + stopComponents(threw); + } + } + /** * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting * a component twice is not authorized. */ - public final ComponentContainer startComponents() { - doBeforeStart(); - pico.start(); - doAfterStart(); - return this; + public ComponentContainer startComponents() { + try { + doBeforeStart(); + pico.start(); + doAfterStart(); + return this; + } catch (Exception e) { + throw PicoUtils.propagate(e); + } } + /** + * This method aims to be overridden + */ + protected void doBeforeStart() { } + /** + * This method aims to be overridden + */ protected void doAfterStart() { } @@ -88,15 +112,29 @@ public class ComponentContainer implements BatchComponent, ServerComponent { * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting * a component twice is not authorized. */ - public final ComponentContainer stopComponents() { - pico.stop(); + public ComponentContainer stopComponents() { + return stopComponents(false); + } + + public ComponentContainer stopComponents(boolean swallowException) { + try { + pico.stop(); + if (parent != null) { + parent.removeChild(); + } + } catch (RuntimeException e) { + if (!swallowException) { + throw PicoUtils.propagate(e); + } + LoggerFactory.getLogger(getClass()).error("Fail to stop container", e); + } return this; } /** * @since 3.5 */ - public final ComponentContainer add(Object... objects) { + public ComponentContainer add(Object... objects) { for (Object object : objects) { if (object instanceof ComponentAdapter) { addPicoAdapter((ComponentAdapter) object); @@ -109,7 +147,7 @@ public class ComponentContainer implements BatchComponent, ServerComponent { return this; } - public final ComponentContainer addSingleton(Object component) { + public ComponentContainer addSingleton(Object component) { return addComponent(component, true); } @@ -117,44 +155,40 @@ public class ComponentContainer implements BatchComponent, ServerComponent { * @param singleton return always the same instance if true, else a new instance * is returned each time the component is requested */ - public final ComponentContainer addComponent(Object component, boolean singleton) { + public ComponentContainer addComponent(Object component, boolean singleton) { pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component); declareExtension(null, component); return this; } - public final ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) { + public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) { pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension); declareExtension(plugin, extension); return this; } - public final void declareExtension(@Nullable PluginMetadata plugin, Object extension) { + public void declareExtension(@Nullable PluginMetadata plugin, Object extension) { propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : ""); } - public final ComponentContainer addPicoAdapter(ComponentAdapter adapter) { + public ComponentContainer addPicoAdapter(ComponentAdapter adapter) { pico.addAdapter(adapter); return this; } - public <T> T get(Class<T> key) { - return pico.getComponent(key); - } - - public final <T> T getComponentByType(Class<T> tClass) { + public <T> T getComponentByType(Class<T> tClass) { return pico.getComponent(tClass); } - public final Object getComponentByKey(Object key) { + public Object getComponentByKey(Object key) { return pico.getComponent(key); } - public final <T> java.util.List<T> getComponentsByType(java.lang.Class<T> tClass) { + public <T> List<T> getComponentsByType(Class<T> tClass) { return pico.getComponents(tClass); } - public final ComponentContainer removeChild() { + public ComponentContainer removeChild() { if (child != null) { pico.removeChildContainer(child.pico); child = null; @@ -162,7 +196,7 @@ public class ComponentContainer implements BatchComponent, ServerComponent { return this; } - public final ComponentContainer createChild() { + public ComponentContainer createChild() { return new ComponentContainer(this); } diff --git a/sonar-core/src/main/java/org/sonar/core/PicoUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/PicoUtils.java index 8a5599b1431..7f7c9544d7c 100644 --- a/sonar-core/src/main/java/org/sonar/core/PicoUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/PicoUtils.java @@ -17,17 +17,17 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.core; +package org.sonar.api.platform; import com.google.common.base.Throwables; import org.picocontainer.PicoLifecycleException; -public final class PicoUtils { +class PicoUtils { private PicoUtils() { } - public static Throwable sanitize(Throwable t) { + static Throwable sanitize(Throwable t) { Throwable result = t; Throwable cause = t.getCause(); if (t instanceof PicoLifecycleException && cause != null) { @@ -36,12 +36,11 @@ public final class PicoUtils { } else { result = cause; } - } return result; } - public static void propagateStartupException(Throwable t) { + static RuntimeException propagate(Throwable t) { throw Throwables.propagate(sanitize(t)); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java index f880578a923..6722b6e550c 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java @@ -19,13 +19,15 @@ */ package org.sonar.api.platform; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.picocontainer.Startable; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.Property; import org.sonar.api.config.PropertyDefinitions; import java.util.Arrays; -import java.util.List; import static junit.framework.Assert.assertTrue; import static org.fest.assertions.Assertions.assertThat; @@ -35,6 +37,9 @@ import static org.mockito.Mockito.verify; public class ComponentContainerTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Test public void shouldRegisterItself() { ComponentContainer container = new ComponentContainer(); @@ -134,29 +139,72 @@ public class ComponentContainerTest { PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); assertThat(propertyDefinitions.get("foo")).isNotNull(); assertThat(container.getComponentByType(ComponentWithProperty.class)).isNotNull(); + assertThat(container.getComponentByKey(ComponentWithProperty.class)).isNotNull(); } @Test public void test_add_class() { ComponentContainer container = new ComponentContainer(); container.add(ComponentWithProperty.class, SimpleComponent.class); - assertThat(container.get(ComponentWithProperty.class)).isNotNull(); - assertThat(container.get(SimpleComponent.class)).isNotNull(); + assertThat(container.getComponentByType(ComponentWithProperty.class)).isNotNull(); + assertThat(container.getComponentByType(SimpleComponent.class)).isNotNull(); } @Test public void test_add_collection() { ComponentContainer container = new ComponentContainer(); container.add(Arrays.asList(ComponentWithProperty.class, SimpleComponent.class)); - assertThat(container.get(ComponentWithProperty.class)).isNotNull(); - assertThat(container.get(SimpleComponent.class)).isNotNull(); + assertThat(container.getComponentByType(ComponentWithProperty.class)).isNotNull(); + assertThat(container.getComponentByType(SimpleComponent.class)).isNotNull(); } @Test public void test_add_adapter() { ComponentContainer container = new ComponentContainer(); container.add(new SimpleComponentProvider()); - assertThat(container.get(SimpleComponent.class)).isNotNull(); + assertThat(container.getComponentByType(SimpleComponent.class)).isNotNull(); + } + + @Test + public void should_fail_to_start_execution() { + ComponentContainer container = new ComponentContainer(); + container.add(UnstartableComponent.class); + + // do not expect a PicoException + thrown.expect(IllegalStateException.class); + container.startComponents(); + } + + @Test + public void stop_exception_should_not_hide_start_exception() { + ComponentContainer container = new ComponentContainer(); + container.add(UnstartableComponent.class, UnstoppableComponent.class); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Fail to start"); + container.execute(); + } + + @Test + public void should_fail_to_stop_execution() { + ComponentContainer container = new ComponentContainer(); + container.add(UnstoppableComponent.class); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Fail to stop"); + container.execute(); + } + + @Test + public void should_execute_components() { + ComponentContainer container = new ComponentContainer(); + Startable component = mock(Startable.class); + container.add(component); + + container.execute(); + + verify(component).start(); + verify(component).stop(); } public static class StartableComponent { @@ -171,6 +219,25 @@ public class ComponentContainerTest { } } + public static class UnstartableComponent { + public void start() { + throw new IllegalStateException("Fail to start"); + } + + public void stop() { + + } + } + + public static class UnstoppableComponent { + public void start() { + } + + public void stop() { + throw new IllegalStateException("Fail to stop"); + } + } + @Property(key = "foo", defaultValue = "bar", name = "Foo") public static class ComponentWithProperty { diff --git a/sonar-core/src/test/java/org/sonar/core/PicoUtilsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/platform/PicoUtilsTest.java index 336a3706963..672608fd21b 100644 --- a/sonar-core/src/test/java/org/sonar/core/PicoUtilsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/platform/PicoUtilsTest.java @@ -17,73 +17,74 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.core; +package org.sonar.api.platform; -import org.hamcrest.core.Is; import org.junit.Test; +import org.picocontainer.Characteristics; +import org.picocontainer.MutablePicoContainer; import org.picocontainer.PicoLifecycleException; -import org.sonar.api.platform.ComponentContainer; import java.io.IOException; -import static org.junit.Assert.assertThat; +import static org.fest.assertions.Assertions.assertThat; import static org.junit.Assert.fail; + public class PicoUtilsTest { @Test public void shouldSanitizePicoLifecycleException() { Throwable th = PicoUtils.sanitize(newPicoLifecycleException(false)); - assertThat(th, Is.is(IllegalStateException.class)); - assertThat(th.getMessage(), Is.is("A good reason to fail")); + assertThat(th).isInstanceOf(IllegalStateException.class); + assertThat(th.getMessage()).isEqualTo("A good reason to fail"); } @Test public void shouldSanitizePicoLifecycleException_no_wrapper_message() { Throwable th = PicoUtils.sanitize(new PicoLifecycleException(null, null, new IllegalStateException("msg"))); - assertThat(th, Is.is(IllegalStateException.class)); - assertThat(th.getMessage(), Is.is("msg")); + assertThat(th).isInstanceOf(IllegalStateException.class); + assertThat(th.getMessage()).isEqualTo("msg"); } @Test public void shouldNotSanitizeOtherExceptions() { Throwable th = PicoUtils.sanitize(new IllegalArgumentException("foo")); - assertThat(th, Is.is(IllegalArgumentException.class)); - assertThat(th.getMessage(), Is.is("foo")); + assertThat(th).isInstanceOf(IllegalArgumentException.class); + assertThat(th.getMessage()).isEqualTo("foo"); } @Test public void shouldPropagateInitialUncheckedException() { try { - PicoUtils.propagateStartupException(newPicoLifecycleException(false)); + PicoUtils.propagate(newPicoLifecycleException(false)); fail(); } catch (RuntimeException e) { - assertThat(e, Is.is(IllegalStateException.class)); + assertThat(e).isInstanceOf(IllegalStateException.class); } } @Test public void shouldThrowUncheckedExceptionWhenPropagatingCheckedException() { try { - PicoUtils.propagateStartupException(newPicoLifecycleException(true)); + PicoUtils.propagate(newPicoLifecycleException(true)); fail(); } catch (RuntimeException e) { - assertThat(e.getCause(), Is.is(IOException.class)); - assertThat(e.getCause().getMessage(), Is.is("Checked")); + assertThat(e.getCause()).isInstanceOf(IOException.class); + assertThat(e.getCause().getMessage()).isEqualTo("Checked"); } } private PicoLifecycleException newPicoLifecycleException(boolean initialCheckedException) { - ComponentContainer componentContainer = new ComponentContainer(); + MutablePicoContainer container = ComponentContainer.createPicoContainer().as(Characteristics.CACHE); if (initialCheckedException) { - componentContainer.addSingleton(CheckedFailureComponent.class); + container.addComponent(CheckedFailureComponent.class); } else { - componentContainer.addSingleton(UncheckedFailureComponent.class); + container.addComponent(UncheckedFailureComponent.class); } try { - componentContainer.startComponents(); + container.start(); return null; } catch (PicoLifecycleException e) { diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index aa73d07d316..b9ac8c908d7 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -35,7 +35,6 @@ import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.TimeProfiler; import org.sonar.api.utils.UriReader; import org.sonar.api.workflow.internal.DefaultWorkflow; -import org.sonar.core.PicoUtils; import org.sonar.core.component.SnapshotPerspectives; import org.sonar.core.config.Logback; import org.sonar.core.i18n.GwtI18n; @@ -141,9 +140,8 @@ public final class Platform { } catch (RuntimeException e) { // full stacktrace is lost by jruby. It must be logged now. - Throwable initialException = PicoUtils.sanitize(e); - LoggerFactory.getLogger(getClass()).error(initialException.getMessage(), initialException); - PicoUtils.propagateStartupException(initialException); + LoggerFactory.getLogger(getClass()).error(e.getMessage(), e); + throw e; } } } @@ -159,9 +157,8 @@ public final class Platform { profiler.stop(); } catch (RuntimeException e) { // full stacktrace is lost by jruby. It must be logged now. - Throwable initialException = PicoUtils.sanitize(e); - LoggerFactory.getLogger(getClass()).error(initialException.getMessage(), initialException); - PicoUtils.propagateStartupException(initialException); + LoggerFactory.getLogger(getClass()).error(e.getMessage(), e); + throw e; } } } |