diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-05-25 11:00:03 +0200 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2011-05-25 23:56:22 +0200 |
commit | 27b6358cba309925505e09f4d44d3157435bf096 (patch) | |
tree | d6c4e61d472e4e8abe4cbf0093f786d53bf58968 /sonar-batch | |
parent | afb886f523968dbdbd4ee7a3ee6a85052e12bde9 (diff) | |
download | sonarqube-27b6358cba309925505e09f4d44d3157435bf096.tar.gz sonarqube-27b6358cba309925505e09f4d44d3157435bf096.zip |
SONAR-2469 refactor Module
SONAR-2469 instanciation strategy of batch extensions
SONAR-2469 fix initialization of project
Diffstat (limited to 'sonar-batch')
18 files changed, 1267 insertions, 499 deletions
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 623192824fe..685b56faf65 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/Batch.java @@ -20,147 +20,29 @@ package org.sonar.batch; import org.apache.commons.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.Plugins; -import org.sonar.api.resources.Project; -import org.sonar.api.utils.HttpDownloader; -import org.sonar.api.utils.ServerHttpClient; -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.core.components.CacheMetricFinder; -import org.sonar.core.components.CacheRuleFinder; -import org.sonar.core.plugin.JpaPluginDao; -import org.sonar.jpa.dao.MeasuresDao; -import org.sonar.jpa.session.DatabaseSessionProvider; -import org.sonar.jpa.session.DriverDatabaseConnector; -import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; +import org.sonar.batch.bootstrap.BootstrapModule; +import org.sonar.batch.bootstrap.Module; -import java.net.URLClassLoader; -import java.util.Arrays; +public final class Batch { -public class Batch { + private Module bootstrapModule; - private static final Logger LOG = LoggerFactory.getLogger(Batch.class); - - private Configuration configuration; - private Object[] components; + public Batch(Configuration configuration, Object... bootstrapperComponents) { + this.bootstrapModule = new BootstrapModule(configuration, bootstrapperComponents).init(); + } - public Batch(Configuration configuration, Object... components) { - this.configuration = configuration; - this.components = components; + /** + * for unit tests + */ + Batch(Module bootstrapModule) { + this.bootstrapModule = bootstrapModule; } public void execute() { - Module bootstrapComponents = null; try { - bootstrapComponents = new BootstrapComponents().init().start(); - analyzeModules(bootstrapComponents); + bootstrapModule.start(); } finally { - if (bootstrapComponents != null) { - try { - bootstrapComponents.stop(); - } catch (Exception e) { - // See http://jira.codehaus.org/browse/SONAR-2346 - // This exception must not override the exception thrown during start() phase. - LOG.error("Fail to stop IoC container", e); - } - } - } - } - - private void analyzeModules(Module bootstrapComponents) { - Module batchComponents = bootstrapComponents.installChild(new BatchComponents()); - batchComponents.start(); - - ProjectTree projectTree = batchComponents.getComponent(ProjectTree.class); - DefaultIndex index = batchComponents.getComponent(DefaultIndex.class); - analyzeModule(batchComponents, index, projectTree.getRootProject()); - - // batchContainer is stopped by its parent - } - - private static class BatchComponents extends Module { - @Override - protected void configure() { - addComponent(ProjectTree.class); - addComponent(DefaultResourceCreationLock.class); - addComponent(DefaultIndex.class); - addComponent(DefaultPersistenceManager.class); - addComponent(DependencyPersister.class); - addComponent(EventPersister.class); - addComponent(LinkPersister.class); - addComponent(MeasurePersister.class); - addComponent(MemoryOptimizer.class); - addComponent(DefaultResourcePersister.class); - addComponent(SourcePersister.class); - addComponent(ViolationPersister.class); - addComponent(JpaPluginDao.class); - addComponent(BatchPluginRepository.class); - addComponent(Plugins.class); - addComponent(ServerHttpClient.class); - addComponent(MeasuresDao.class); - addComponent(CacheRuleFinder.class); - addComponent(CacheMetricFinder.class); - addComponent(PastSnapshotFinderByDate.class); - addComponent(PastSnapshotFinderByDays.class); - addComponent(PastSnapshotFinderByPreviousAnalysis.class); - addComponent(PastSnapshotFinderByVersion.class); - addComponent(PastMeasuresLoader.class); - addComponent(PastSnapshotFinder.class); + bootstrapModule.stop(); } } - - private class BootstrapComponents extends Module { - @Override - protected void configure() { - addComponent(configuration); - addComponent(ServerMetadata.class);// registered here because used by BootstrapClassLoader - addComponent(TempDirectories.class);// registered here because used by BootstrapClassLoader - addComponent(HttpDownloader.class);// registered here because used by BootstrapClassLoader - addComponent(ExtensionDownloader.class);// registered here because used by BootstrapClassLoader - addComponent(BootstrapClassLoader.class); - - URLClassLoader bootstrapClassLoader = getComponent(BootstrapClassLoader.class).getClassLoader(); - // set as the current context classloader for hibernate, else it does not find the JDBC driver. - Thread.currentThread().setContextClassLoader(bootstrapClassLoader); - - addComponent(new DriverDatabaseConnector(configuration, bootstrapClassLoader)); - addComponent(ThreadLocalDatabaseSessionFactory.class); - addAdapter(new DatabaseSessionProvider()); - for (Object component : components) { - addComponent(component); - } - if (!isMavenPluginExecutorRegistered()) { - addComponent(FakeMavenPluginExecutor.class); - } - } - } - - boolean isMavenPluginExecutorRegistered() { - for (Object component : components) { - if (component instanceof Class && MavenPluginExecutor.class.isAssignableFrom((Class<?>) component)) { - return true; - } - } - return false; - } - - private void analyzeModule(Module batchComponents, DefaultIndex index, Project project) { - for (Project module : project.getModules()) { - analyzeModule(batchComponents, index, module); - } - LOG.info("------------- Analyzing {}", project.getName()); - - String[] exclusionPatterns = project.getExclusionPatterns(); - if (exclusionPatterns != null && exclusionPatterns.length > 0) { - LOG.info("Excluded sources : {}", Arrays.toString(exclusionPatterns)); - } - - new ProjectBatch(batchComponents).execute(index, project); - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java deleted file mode 100644 index 2216a49c1b5..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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; - -import org.apache.maven.project.MavenProject; -import org.sonar.api.batch.BatchExtensionDictionnary; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.Metrics; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectFileSystem; -import org.sonar.api.rules.DefaultRulesManager; -import org.sonar.api.utils.SonarException; -import org.sonar.batch.bootstrap.BatchPluginRepository; -import org.sonar.batch.bootstrapper.ProjectDefinition; -import org.sonar.batch.components.PastViolationsLoader; -import org.sonar.batch.components.TimeMachineConfiguration; -import org.sonar.batch.events.EventBus; -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.*; - -public class ProjectBatch { - - private Module globalComponents; - - public ProjectBatch(Module globalComponents) { - this.globalComponents = globalComponents; - } - - public void execute(DefaultIndex index, Project project) { - Module projectComponents = null; - try { - projectComponents = startChildContainer(index, project); - - projectComponents.getComponent(Phases.class).execute(project); - - } finally { - if (projectComponents != null) { - try { - globalComponents.uninstallChild(projectComponents); - projectComponents.stop(); - } catch (Exception e) { - // do not log - } - } - } - } - - public Module startChildContainer(DefaultIndex index, Project project) { - Module projectComponents = globalComponents.installChild(new ProjectComponents(project)); - projectComponents.install(new ProjectCoreComponents()); - projectComponents.start(); - - // post-initializations - - Language language = projectComponents.getComponent(Languages.class).get(project.getLanguageKey()); - if (language == null) { - throw new SonarException("Language with key '" + project.getLanguageKey() + "' not found"); - } - project.setLanguage(language); - - index.setCurrentProject(project, - projectComponents.getComponent(ResourceFilters.class), - projectComponents.getComponent(ViolationFilters.class), - projectComponents.getComponent(RulesProfile.class)); - - // TODO See http://jira.codehaus.org/browse/SONAR-2126 - // previously MavenProjectBuilder was responsible for creation of ProjectFileSystem - project.setFileSystem(projectComponents.getComponent(ProjectFileSystem.class)); - - return projectComponents; - } - - private static class ProjectComponents extends Module { - private Project project; - - public ProjectComponents(Project project) { - this.project = project; - } - - @Override - protected void configure() { - ProjectDefinition projectDefinition = getComponent(ProjectTree.class).getProjectDefinition(project); - addComponent(projectDefinition); - for (Object component : projectDefinition.getContainerExtensions()) { - addComponent(component); - if (component instanceof MavenProject) { - // For backward compatibility we must set POM and actual packaging - MavenProject pom = (MavenProject) component; - project.setPom(pom); - project.setPackaging(pom.getPackaging()); - } - } - - addComponent(project); - addComponent(DefaultProjectClasspath.class); - addComponent(DefaultProjectFileSystem2.class); - addComponent(project.getConfiguration()); - - // need to be registered after the Configuration - getComponent(BatchPluginRepository.class).registerPlugins(getContainer()); - - addComponent(DaoFacade.class); - addComponent(RulesDao.class); - - // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) - addComponent(getComponent(DefaultResourcePersister.class).getSnapshot(project)); - - addComponent(org.sonar.api.database.daos.MeasuresDao.class); - addComponent(ProfilesDao.class); - addComponent(AsyncMeasuresDao.class); - addComponent(AsyncMeasuresService.class); - addComponent(DefaultRulesManager.class); - addComponent(DefaultSensorContext.class); - addComponent(Languages.class); - addComponent(BatchExtensionDictionnary.class); - addComponent(DefaultTimeMachine.class); - addComponent(ViolationFilters.class); - addComponent(ResourceFilters.class); - addComponent(DefaultModelFinder.class); - addComponent(TimeMachineConfiguration.class); - addComponent(PastViolationsLoader.class); - addComponent(ProfileLoader.class, DefaultProfileLoader.class); - - addAdapter(new ProfileProvider()); - addAdapter(new CheckProfileProvider()); - } - } - - private static class ProjectCoreComponents extends Module { - @Override - protected void configure() { - addComponent(EventBus.class); - addComponent(Phases.class); - addComponent(PhasesTimeProfiler.class); - for (Class clazz : Phases.getPhaseClasses()) { - addComponent(clazz); - } - for (Metric metric : CoreMetrics.getMetrics()) { - addComponent(metric.getKey(), metric); - } - for (Metrics metricRepo : getComponents(Metrics.class)) { - for (Metric metric : metricRepo.getMetrics()) { - addComponent(metric.getKey(), metric); - } - } - } - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionInstaller.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionInstaller.java new file mode 100644 index 00000000000..d111f444ec4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionInstaller.java @@ -0,0 +1,74 @@ +/* + * 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.bootstrap; + +import org.sonar.api.BatchComponent; +import org.sonar.api.ExtensionProvider; +import org.sonar.api.Plugin; +import org.sonar.api.batch.CoverageExtension; +import org.sonar.api.batch.InstanciationStrategy; +import org.sonar.batch.bootstrapper.EnvironmentInformation; + +import java.util.List; + +public final class BatchExtensionInstaller implements BatchComponent { + + private BatchPluginRepository pluginRepository; + private EnvironmentInformation environment; + + public BatchExtensionInstaller(BatchPluginRepository pluginRepository, EnvironmentInformation environment) { + this.pluginRepository = pluginRepository; + this.environment = environment; + } + + public void install(Module module) { + for (Plugin plugin : pluginRepository.getPlugins()) { + for (Object extension : plugin.getExtensions()) { + installExtension(module, extension); + } + } + installExtensionProviders(module); + } + + void installExtensionProviders(Module module) { + List<ExtensionProvider> providers = module.getComponents(ExtensionProvider.class); + for (ExtensionProvider provider : providers) { + Object obj = provider.provide(); + if (obj instanceof Iterable) { + for (Object extension : (Iterable) obj) { + installExtension(module, extension); + } + } else { + installExtension(module, obj); + } + } + } + + void installExtension(Module module, Object extension) { + if (ExtensionUtils.isBatchExtension(extension) && + ExtensionUtils.isSupportedEnvironment(extension, environment) && + ExtensionUtils.isInstantiationStrategy(extension, InstanciationStrategy.PER_BATCH)) { + if (ExtensionUtils.isType(extension, CoverageExtension.class)) { + throw new IllegalArgumentException("Instantiation strategy " + InstanciationStrategy.PER_BATCH + " is not supported on CoverageExtension components: " + extension); + } + module.addComponent(extension); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java new file mode 100644 index 00000000000..c69b34c6b82 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java @@ -0,0 +1,89 @@ +/* + * 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.bootstrap; + +import org.sonar.api.Plugins; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.ServerHttpClient; +import org.sonar.batch.DefaultResourceCreationLock; +import org.sonar.batch.ProjectTree; +import org.sonar.batch.components.*; +import org.sonar.batch.index.*; +import org.sonar.core.components.CacheMetricFinder; +import org.sonar.core.components.CacheRuleFinder; +import org.sonar.jpa.dao.MeasuresDao; + +/** + * Level-2 components. Connected to database. + */ +public class BatchModule extends Module { + @Override + protected void configure() { + addComponent(ProjectTree.class); + addComponent(DefaultResourceCreationLock.class); + addComponent(DefaultIndex.class); + addComponent(DefaultPersistenceManager.class); + addComponent(DependencyPersister.class); + addComponent(EventPersister.class); + addComponent(LinkPersister.class); + addComponent(MeasurePersister.class); + addComponent(MemoryOptimizer.class); + addComponent(DefaultResourcePersister.class); + addComponent(SourcePersister.class); + addComponent(ViolationPersister.class); + addComponent(Plugins.class); + addComponent(ServerHttpClient.class); + addComponent(MeasuresDao.class); + addComponent(CacheRuleFinder.class); + addComponent(CacheMetricFinder.class); + addComponent(PastSnapshotFinderByDate.class); + addComponent(PastSnapshotFinderByDays.class); + addComponent(PastSnapshotFinderByPreviousAnalysis.class); + addComponent(PastSnapshotFinderByVersion.class); + addComponent(PastMeasuresLoader.class); + addComponent(PastSnapshotFinder.class); + addBatchExtensions(); + } + + private void addBatchExtensions() { + BatchExtensionInstaller installer = getComponent(BatchExtensionInstaller.class); + installer.install(this); + } + + @Override + protected void doStart() { + ProjectTree projectTree = getComponent(ProjectTree.class); + analyze(projectTree.getRootProject()); + } + + private void analyze(Project project) { + for (Project subProject : project.getModules()) { + analyze(subProject); + } + + Module projectComponents = installChild(new ProjectModule(project)); + try { + projectComponents.start(); + } finally { + projectComponents.stop(); + uninstallChild(projectComponents); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java index c883998d927..c9dce2513c8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java @@ -19,76 +19,47 @@ */ package org.sonar.batch.bootstrap; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; - import com.google.common.collect.Lists; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.ArrayUtils; +import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; -import org.picocontainer.MutablePicoContainer; -import org.picocontainer.PicoContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.BatchExtension; import org.sonar.api.Plugin; -import org.sonar.api.batch.AbstractCoverageExtension; -import org.sonar.api.batch.CoverageExtension; -import org.sonar.api.batch.SupportedEnvironment; -import org.sonar.api.resources.Java; -import org.sonar.api.resources.Project; -import org.sonar.api.utils.AnnotationUtils; +import org.sonar.api.Properties; +import org.sonar.api.Property; +import org.sonar.api.platform.PluginRepository; import org.sonar.api.utils.SonarException; -import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.core.classloaders.ClassLoadersCollection; -import org.sonar.core.plugin.AbstractPluginRepository; import org.sonar.core.plugin.JpaPlugin; import org.sonar.core.plugin.JpaPluginDao; import org.sonar.core.plugin.JpaPluginFile; -public class BatchPluginRepository extends AbstractPluginRepository { +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class BatchPluginRepository implements PluginRepository { private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class); private JpaPluginDao dao; + private ExtensionDownloader artifactDownloader; + private Map<String, Plugin> pluginsByKey; - private ClassLoadersCollection classLoaders; - private ExtensionDownloader extensionDownloader; - private EnvironmentInformation environment; - private List<JpaPlugin> register; - - public BatchPluginRepository(JpaPluginDao dao, ExtensionDownloader extensionDownloader, EnvironmentInformation environment) { + public BatchPluginRepository(JpaPluginDao dao, ExtensionDownloader artifactDownloader) { this.dao = dao; - this.extensionDownloader = extensionDownloader; - this.environment = environment; - LOG.info("Execution environment: {} {}", environment.getKey(), environment.getVersion()); - } - - /** - * for unit tests only - */ - BatchPluginRepository() { - } - - private List<URL> download(JpaPlugin pluginMetadata) { - List<URL> urls = Lists.newArrayList(); - for (JpaPluginFile pluginFile : pluginMetadata.getFiles()) { - File file = extensionDownloader.downloadExtension(pluginFile); - try { - urls.add(file.toURI().toURL()); - - } catch (MalformedURLException e) { - throw new SonarException("Can not get the URL of: " + file, e); - } - } - return urls; + this.artifactDownloader = artifactDownloader; +// TODO reactivate somewhere else: LOG.info("Execution environment: {} {}", environment.getKey(), environment.getVersion()); } public void start() { - register = Lists.newArrayList(); - classLoaders = new ClassLoadersCollection(Thread.currentThread().getContextClassLoader()); + List<JpaPlugin> pluginsMetadata = Lists.newArrayList(); + pluginsByKey = Maps.newHashMap(); + ClassLoadersCollection classLoaders = new ClassLoadersCollection(Thread.currentThread().getContextClassLoader()); List<JpaPlugin> jpaPlugins = dao.getPlugins(); @@ -97,7 +68,7 @@ public class BatchPluginRepository extends AbstractPluginRepository { String key = pluginMetadata.getKey(); List<URL> urls = download(pluginMetadata); classLoaders.createClassLoader(key, urls, pluginMetadata.isUseChildFirstClassLoader() == Boolean.TRUE); - register.add(pluginMetadata); + pluginsMetadata.add(pluginMetadata); } } @@ -110,7 +81,8 @@ public class BatchPluginRepository extends AbstractPluginRepository { LOG.debug("Plugin {} extends {}", pluginKey, basePluginKey); List<URL> urls = download(pluginMetadata); classLoaders.extend(basePluginKey, pluginKey, urls); - register.add(pluginMetadata); + pluginsMetadata.add(pluginMetadata); + } else { // Ignored, because base plugin doesn't exists LOG.warn("Plugin {} extends nonexistent plugin {}", pluginKey, basePluginKey); @@ -118,65 +90,54 @@ public class BatchPluginRepository extends AbstractPluginRepository { } } - classLoaders.done(); - } - - public void registerPlugins(MutablePicoContainer pico) { - for (JpaPlugin pluginMetadata : register) { + for (JpaPlugin pluginMetadata : jpaPlugins) { try { Class claz = classLoaders.get(pluginMetadata.getKey()).loadClass(pluginMetadata.getPluginClass()); Plugin plugin = (Plugin) claz.newInstance(); - registerPlugin(pico, plugin, pluginMetadata.getKey()); + pluginsByKey.put(pluginMetadata.getKey(), plugin); } catch (Exception e) { - throw new SonarException("Fail to load extensions from plugin " + pluginMetadata.getKey(), e); + throw new SonarException("Fail to load plugin " + pluginMetadata.getKey(), e); } } - invokeExtensionProviders(pico); + + classLoaders.done(); } - @Override - protected boolean shouldRegisterExtension(PicoContainer container, String pluginKey, Object extension) { - boolean ok = isType(extension, BatchExtension.class); - if (ok && !isSupportsEnvironment(extension)) { - ok = false; - LOG.debug("The following extension is ignored: " + extension + " due to execution environment."); - } - if (ok && isType(extension, CoverageExtension.class)) { - ok = shouldRegisterCoverageExtension(pluginKey, container.getComponent(Project.class), container.getComponent(Configuration.class)); - if (!ok) { - LOG.debug("The following extension is ignored: " + extension + ". See the parameter " + AbstractCoverageExtension.PARAM_PLUGIN); + private List<URL> download(JpaPlugin pluginMetadata) { + List<URL> urls = Lists.newArrayList(); + for (JpaPluginFile pluginFile : pluginMetadata.getFiles()) { + File file = artifactDownloader.downloadExtension(pluginFile); + try { + urls.add(file.toURI().toURL()); + + } catch (MalformedURLException e) { + throw new SonarException("Can not get the URL of: " + file, e); } } - return ok; + return urls; } - private boolean isSupportsEnvironment(Object extension) { - Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass()); - SupportedEnvironment env = AnnotationUtils.getClassAnnotation(clazz, SupportedEnvironment.class); - if (env == null) { - return true; - } - for (String supported : env.value()) { - if (StringUtils.equalsIgnoreCase(environment.getKey(), supported)) { - return true; - } - } - return false; + public Collection<Plugin> getPlugins() { + return pluginsByKey.values(); } - boolean shouldRegisterCoverageExtension(String pluginKey, Project project, Configuration conf) { - if (!project.getAnalysisType().isDynamic(true)) { - // not dynamic and not reuse reports - return false; - } - if (StringUtils.equals(project.getLanguageKey(), Java.KEY)) { - String[] selectedPluginKeys = conf.getStringArray(AbstractCoverageExtension.PARAM_PLUGIN); - if (ArrayUtils.isEmpty(selectedPluginKeys)) { - selectedPluginKeys = new String[] { AbstractCoverageExtension.DEFAULT_PLUGIN }; + public Plugin getPlugin(String key) { + return pluginsByKey.get(key); + } + + public Map<String, Plugin> getPluginsByKey() { + return Collections.unmodifiableMap(pluginsByKey); + } + + // TODO remove this method. Not used in batch. + public Property[] getProperties(Plugin plugin) { + if (plugin != null) { + Class<? extends Plugin> classInstance = plugin.getClass(); + if (classInstance.isAnnotationPresent(Properties.class)) { + return classInstance.getAnnotation(Properties.class).value(); } - return ArrayUtils.contains(selectedPluginKeys, pluginKey); } - return true; + return new Property[0]; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java new file mode 100644 index 00000000000..fc9c344412a --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java @@ -0,0 +1,104 @@ +/* + * 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.bootstrap; + +import org.apache.commons.configuration.Configuration; +import org.sonar.api.Plugin; +import org.sonar.api.utils.HttpDownloader; +import org.sonar.batch.FakeMavenPluginExecutor; +import org.sonar.batch.MavenPluginExecutor; +import org.sonar.batch.ServerMetadata; +import org.sonar.core.plugin.JpaPluginDao; +import org.sonar.jpa.session.DatabaseSessionProvider; +import org.sonar.jpa.session.DriverDatabaseConnector; +import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; + +import java.net.URLClassLoader; + +/** + * Level 1 components + */ +public class BootstrapModule extends Module { + + private Configuration configuration; + private Object[] boostrapperComponents; + + public BootstrapModule(Configuration configuration, Object... boostrapperComponents) { + this.configuration = configuration; + this.boostrapperComponents = boostrapperComponents; + } + + @Override + protected void configure() { + addComponent(configuration); + addComponent(ServerMetadata.class);// registered here because used by BootstrapClassLoader + addComponent(TempDirectories.class);// registered here because used by BootstrapClassLoader + addComponent(HttpDownloader.class);// registered here because used by BootstrapClassLoader + addComponent(ExtensionDownloader.class);// registered here because used by BootstrapClassLoader + addComponent(BootstrapClassLoader.class); + + URLClassLoader bootstrapClassLoader = getComponent(BootstrapClassLoader.class).getClassLoader(); + // set as the current context classloader for hibernate, else it does not find the JDBC driver. + Thread.currentThread().setContextClassLoader(bootstrapClassLoader); + + addComponent(new DriverDatabaseConnector(configuration, bootstrapClassLoader)); + addComponent(ThreadLocalDatabaseSessionFactory.class); + addAdapter(new DatabaseSessionProvider()); + for (Object component : boostrapperComponents) { + addComponent(component); + } + if (!isMavenPluginExecutorRegistered()) { + addComponent(FakeMavenPluginExecutor.class); + } + + // LIMITATION : list of plugins to download is currently loaded from database. It should be loaded from + // remote HTTP index. + addComponent(JpaPluginDao.class); + addComponent(BatchPluginRepository.class); + addComponent(BatchExtensionInstaller.class); + addComponent(ProjectExtensionInstaller.class); + } + + boolean isMavenPluginExecutorRegistered() { + for (Object component : boostrapperComponents) { + if (component instanceof Class && MavenPluginExecutor.class.isAssignableFrom((Class<?>) component)) { + return true; + } + } + return false; + } + + @Override + protected void doStart() { + addPlugins(); + Module batchComponents = installChild(new BatchModule()); + batchComponents.start(); + } + + private void addPlugins() { + // Plugins have been loaded during the startup of BatchPluginRepository. + // In a perfect world BatchPluginRepository should be a factory which injects new components into container, but + // (it seems that) this feature does not exist in PicoContainer. + // Limitation: the methods start() and stop() are not called on org.sonar.api.Plugin instances. + for (Plugin plugin : getComponent(BatchPluginRepository.class).getPlugins()) { + addComponent(plugin); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionDownloader.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionDownloader.java index 0cb1a95b53c..49ec936b24d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionDownloader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionDownloader.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.bootstrap; +import org.sonar.api.BatchComponent; import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.SonarException; import org.sonar.batch.ServerMetadata; @@ -28,7 +29,10 @@ import java.io.File; import java.net.URI; import java.net.URISyntaxException; -public final class ExtensionDownloader { +/** + * TODO this class should be renamed ArtifactDownloader, because it does not relate only to plugin extensions. + */ +public final class ExtensionDownloader implements BatchComponent { private HttpDownloader httpDownloader; private TempDirectories workingDirectories; diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionUtils.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionUtils.java new file mode 100644 index 00000000000..01a7c809eca --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionUtils.java @@ -0,0 +1,67 @@ +/* + * 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.bootstrap; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchExtension; +import org.sonar.api.Extension; +import org.sonar.api.batch.InstanciationStrategy; +import org.sonar.api.batch.SupportedEnvironment; +import org.sonar.api.utils.AnnotationUtils; +import org.sonar.batch.bootstrapper.EnvironmentInformation; + +public final class ExtensionUtils { + + private ExtensionUtils() { + // only static methods + } + + static boolean isInstantiationStrategy(Object extension, String strategy) { + Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass()); + InstanciationStrategy extStrategy = AnnotationUtils.getClassAnnotation(clazz, InstanciationStrategy.class); + if (extStrategy != null) { + return strategy.equals(extStrategy.value()); + } + return InstanciationStrategy.PER_PROJECT.equals(strategy); + } + + static boolean isBatchExtension(Object extension) { + return isType(extension, BatchExtension.class); + } + + static boolean isSupportedEnvironment(Object extension, EnvironmentInformation environment) { + Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass()); + SupportedEnvironment env = AnnotationUtils.getClassAnnotation(clazz, SupportedEnvironment.class); + if (env == null) { + return true; + } + for (String supported : env.value()) { + if (StringUtils.equalsIgnoreCase(environment.getKey(), supported)) { + return true; + } + } + return false; + } + + static boolean isType(Object extension, Class<? extends Extension> extensionClass) { + Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass()); + return extensionClass.isAssignableFrom(clazz); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/Module.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/Module.java index 6bf82c97051..2c0d289b548 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/Module.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/Module.java @@ -17,18 +17,20 @@ * 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; - -import java.util.List; +package org.sonar.batch.bootstrap; import org.picocontainer.Characteristics; import org.picocontainer.ComponentAdapter; import org.picocontainer.MutablePicoContainer; import org.sonar.api.utils.IocContainer; +import java.util.List; + /** * Module describes group of components - {@link #configure()}. * Several modules can be grouped together - {@link #install(Module)}, {@link #installChild(Module)}. + * <p/> + * TODO Move to org.sonar.batch.bootstrap ? */ public abstract class Module { @@ -38,9 +40,7 @@ public abstract class Module { * @return this */ public final Module init() { - this.container = IocContainer.buildPicoContainer(); - configure(); - return this; + return init(IocContainer.buildPicoContainer()); } /** @@ -54,7 +54,7 @@ public abstract class Module { /** * Installs module into this module. - * + * * @return this */ public final Module install(Module module) { @@ -64,39 +64,53 @@ public abstract class Module { /** * Installs module into new scope - see http://picocontainer.org/scopes.html - * + * * @return installed module */ - public final Module installChild(Module module) { - MutablePicoContainer child = container.makeChildContainer(); + public final Module installChild(Module child) { + MutablePicoContainer childContainer = container.makeChildContainer(); // register container as a component, because it used for example in BatchExtensionDictionnary, // but in fact this is anti-pattern - http://picocontainer.codehaus.org/container-dependency-antipattern.html - child.addComponent(new IocContainer(child)); - child.setName(module.toString()); - module.init(child); - return module; + childContainer.addComponent(new IocContainer(childContainer)); + childContainer.setName(child.toString()); + child.init(childContainer); + return child; } - public final void uninstallChild(Module module) { - container.removeChildContainer(module.container); + public final void uninstallChild(Module child) { + container.removeChildContainer(child.container); } /** * @return this */ - public Module start() { + public final Module start() { container.start(); + doStart(); return this; } + protected void doStart() { + // empty method to be overridden + } + /** * @return this */ - public Module stop() { - container.stop(); + public final Module stop() { + try { + doStop(); + container.stop(); + } catch (Exception e) { + // ignore + } return this; } + protected void doStop() { + // empty method to be overridden + } + /** * Implementation of this method must not contain conditional logic and just should contain several invocations of * {@link #addComponent(Object)}, {@link #addComponent(Object, Object)} or {@link #addAdapter(ComponentAdapter)}. @@ -104,7 +118,11 @@ public abstract class Module { protected abstract void configure(); protected final void addComponent(Object component) { - container.as(Characteristics.CACHE).addComponent(component); + if (component instanceof Class) { + container.as(Characteristics.CACHE).addComponent(component); + } else { + container.as(Characteristics.CACHE).addComponent(component.getClass().getCanonicalName() + "-" + component.toString(), component); + } } protected final void addComponent(Object componentKey, Object component) { @@ -124,7 +142,7 @@ public abstract class Module { } /** - * @TODO should not be used and should be removed + * TODO should not be used and should be removed */ public final MutablePicoContainer getContainer() { return container; diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExtensionInstaller.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExtensionInstaller.java new file mode 100644 index 00000000000..4d3faea35c0 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectExtensionInstaller.java @@ -0,0 +1,105 @@ +/* + * 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.bootstrap; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchComponent; +import org.sonar.api.ExtensionProvider; +import org.sonar.api.Plugin; +import org.sonar.api.batch.AbstractCoverageExtension; +import org.sonar.api.batch.CoverageExtension; +import org.sonar.api.batch.InstanciationStrategy; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrapper.EnvironmentInformation; + +import java.util.List; +import java.util.Map; + +public final class ProjectExtensionInstaller implements BatchComponent { + + private BatchPluginRepository pluginRepository; + private EnvironmentInformation environment; + + public ProjectExtensionInstaller(BatchPluginRepository pluginRepository, EnvironmentInformation environment) { + this.pluginRepository = pluginRepository; + this.environment = environment; + } + + public void install(Module module, Project project) { + for (Map.Entry<String, Plugin> entry : pluginRepository.getPluginsByKey().entrySet()) { + for (Object extension : entry.getValue().getExtensions()) { + installExtension(module, extension, project, entry.getKey()); + } + } + + installExtensionProviders(module, project); + } + + void installExtensionProviders(Module module, Project project) { + List<ExtensionProvider> providers = module.getComponents(ExtensionProvider.class); + for (ExtensionProvider provider : providers) { + Object obj = provider.provide(); + if (obj instanceof Iterable) { + for (Object extension : (Iterable) obj) { + installExtension(module, extension, project, ""); + } + } else { + installExtension(module, obj, project, ""); + } + } + } + + private Object installExtension(Module module, Object extension, Project project, String pluginKey) { + if (ExtensionUtils.isBatchExtension(extension) && + ExtensionUtils.isSupportedEnvironment(extension, environment) && + ExtensionUtils.isInstantiationStrategy(extension, InstanciationStrategy.PER_PROJECT) && + !isDeactivatedCoverageExtension(extension, project, pluginKey)) { + + module.addComponent(extension); + return extension; + } + return null; + } + + /** + * TODO this code is specific to Java projects and should be moved somewhere else + */ + boolean isDeactivatedCoverageExtension(Object extension, Project project, String pluginKey) { + if (!ExtensionUtils.isType(extension, CoverageExtension.class)) { + return false; + } + + if (!project.getAnalysisType().isDynamic(true)) { + // not dynamic and not reuse reports + return true; + } + + if (StringUtils.equals(project.getLanguageKey(), Java.KEY)) { + String[] selectedPluginKeys = project.getConfiguration().getStringArray(AbstractCoverageExtension.PARAM_PLUGIN); + if (ArrayUtils.isEmpty(selectedPluginKeys)) { + selectedPluginKeys = new String[]{AbstractCoverageExtension.DEFAULT_PLUGIN}; + } + return !ArrayUtils.contains(selectedPluginKeys, pluginKey); + } + return false; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectModule.java new file mode 100644 index 00000000000..26c3609cbaf --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectModule.java @@ -0,0 +1,166 @@ +/* + * 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.bootstrap; + +import org.apache.maven.project.MavenProject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.Metrics; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Languages; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.ProjectFileSystem; +import org.sonar.api.rules.DefaultRulesManager; +import org.sonar.api.utils.SonarException; +import org.sonar.batch.*; +import org.sonar.batch.bootstrapper.ProjectDefinition; +import org.sonar.batch.components.PastViolationsLoader; +import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.events.EventBus; +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.*; + +import java.util.Arrays; + +public class ProjectModule extends Module { + private static final Logger LOG = LoggerFactory.getLogger(ProjectModule.class); + private Project project; + + public ProjectModule(Project project) { + this.project = project; + } + + @Override + protected void configure() { + logSettings(); + addCoreComponents(); + addProjectComponents(); + addProjectPluginExtensions(); + } + + + private void addProjectComponents() { + ProjectDefinition projectDefinition = getComponent(ProjectTree.class).getProjectDefinition(project); + addComponent(projectDefinition); + for (Object component : projectDefinition.getContainerExtensions()) { + addComponent(component); + if (component instanceof MavenProject) { + // For backward compatibility we must set POM and actual packaging + MavenProject pom = (MavenProject) component; + project.setPom(pom); + project.setPackaging(pom.getPackaging()); + } + } + + addComponent(project); + addComponent(DefaultProjectClasspath.class); + addComponent(DefaultProjectFileSystem2.class); + addComponent(project.getConfiguration()); + addComponent(DaoFacade.class); + addComponent(RulesDao.class); + + // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) + addComponent(getComponent(DefaultResourcePersister.class).getSnapshot(project)); + addComponent(org.sonar.api.database.daos.MeasuresDao.class); + addComponent(ProfilesDao.class); + addComponent(AsyncMeasuresDao.class); + addComponent(AsyncMeasuresService.class); + addComponent(DefaultRulesManager.class); + addComponent(DefaultSensorContext.class); + addComponent(Languages.class); + addComponent(BatchExtensionDictionnary.class); + addComponent(DefaultTimeMachine.class); + addComponent(ViolationFilters.class); + addComponent(ResourceFilters.class); + addComponent(DefaultModelFinder.class); + addComponent(TimeMachineConfiguration.class); + addComponent(PastViolationsLoader.class); + addComponent(ProfileLoader.class, DefaultProfileLoader.class); + addAdapter(new ProfileProvider()); + addAdapter(new CheckProfileProvider()); + } + + private void addCoreComponents() { + addComponent(EventBus.class); + addComponent(Phases.class); + addComponent(PhasesTimeProfiler.class); + for (Class clazz : Phases.getPhaseClasses()) { + addComponent(clazz); + } + + // TODO move metrics to BatchComponents + for (Metric metric : CoreMetrics.getMetrics()) { + addComponent(metric.getKey(), metric); + } + for (Metrics metricRepo : getComponents(Metrics.class)) { + for (Metric metric : metricRepo.getMetrics()) { + addComponent(metric.getKey(), metric); + } + } + } + + private void addProjectPluginExtensions() { + ProjectExtensionInstaller installer = getComponent(ProjectExtensionInstaller.class); + installer.install(this, project); + } + + + private void logSettings() { + // TODO move these logs in a dedicated component + LOG.info("------------- Analyzing {}", project.getName()); + + String[] exclusionPatterns = project.getExclusionPatterns(); + if (exclusionPatterns != null && exclusionPatterns.length > 0) { + LOG.info("Excluded sources : {}", Arrays.toString(exclusionPatterns)); + } + } + + /** + * Analyze project + */ + @Override + protected void doStart() { + Language language = getComponent(Languages.class).get(project.getLanguageKey()); + if (language == null) { + throw new SonarException("Language with key '" + project.getLanguageKey() + "' not found"); + } + project.setLanguage(language); + + DefaultIndex index = getComponent(DefaultIndex.class); + index.setCurrentProject(project, + getComponent(ResourceFilters.class), + getComponent(ViolationFilters.class), + getComponent(RulesProfile.class)); + + // TODO See http://jira.codehaus.org/browse/SONAR-2126 + // previously MavenProjectBuilder was responsible for creation of ProjectFileSystem + project.setFileSystem(getComponent(ProjectFileSystem.class)); + + getComponent(Phases.class).execute(project); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java b/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java index 9de4aa3df64..094bbb71f87 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java @@ -20,31 +20,43 @@ package org.sonar.batch; import org.junit.Test; -import org.sonar.api.batch.maven.MavenPluginHandler; -import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.Module; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; public class BatchTest { - class MyMavenPluginExecutor implements MavenPluginExecutor { - public void execute(Project project, String goal) { - } + @Test + public void shouldExecute() { + FakeModule module = new FakeModule(); + module.init(); + new Batch(module).execute(); - public MavenPluginHandler execute(Project project, MavenPluginHandler handler) { - return handler; - } + assertThat(module.started, is(true)); + assertThat(module.stopped, is(true)); } - @Test - public void shouldSearchMavenPluginExecutor() { - Batch batch; + public static class FakeModule extends Module { + private boolean started=false; + private boolean stopped=false; - batch = new Batch(null, MyMavenPluginExecutor.class); - assertThat(batch.isMavenPluginExecutorRegistered(), is(true)); + @Override + protected void doStart() { + started = true; + } - batch = new Batch(null); - assertThat(batch.isMavenPluginExecutorRegistered(), is(false)); + @Override + protected void doStop() { + if (!started) { + throw new IllegalStateException("Not started"); + } + stopped = true; + } + + @Override + protected void configure() { + } } + } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionInstallerTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionInstallerTest.java new file mode 100644 index 00000000000..fbd29a90545 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionInstallerTest.java @@ -0,0 +1,129 @@ +/* + * 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.bootstrap; + +import org.junit.Test; +import org.sonar.api.*; +import org.sonar.api.batch.CoverageExtension; +import org.sonar.api.batch.InstanciationStrategy; +import org.sonar.batch.bootstrapper.EnvironmentInformation; + +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BatchExtensionInstallerTest { + + @Test + public void shouldInstallExtensionsWithBatchInstantiationStrategy() { + BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class); + when(pluginRepository.getPlugins()).thenReturn(Arrays.asList((Plugin) new SonarPlugin() { + public List getExtensions() { + return Arrays.asList(BatchService.class, ProjectService.class, ServerService.class); + } + })); + Module module = new FakeModule().init(); + BatchExtensionInstaller installer = new BatchExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7")); + + installer.install(module); + + assertThat(module.getComponent(BatchService.class), not(nullValue())); + assertThat(module.getComponent(ProjectService.class), nullValue()); + assertThat(module.getComponent(ServerService.class), nullValue()); + } + + @Test + public void shouldInstallProvidersWithBatchInstantiationStrategy() { + BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class); + when(pluginRepository.getPlugins()).thenReturn(Arrays.asList((Plugin) new SonarPlugin(){ + public List getExtensions() { + return Arrays.asList(BatchServiceProvider.class, ProjectServiceProvider.class); + } + })); + Module module = new FakeModule().init(); + BatchExtensionInstaller installer = new BatchExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7")); + + installer.install(module); + + assertThat(module.getComponent(BatchService.class), not(nullValue())); + assertThat(module.getComponent(ProjectService.class), nullValue()); + assertThat(module.getComponent(ServerService.class), nullValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotSupportCoverageExtensionsWithBatchInstantiationStrategy() { + // the reason is that CoverageExtensions currently depend on Project + BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class); + when(pluginRepository.getPlugins()).thenReturn(Arrays.asList((Plugin) new SonarPlugin(){ + public List getExtensions() { + return Arrays.asList(InvalidCoverageExtension.class); + } + })); + Module module = new FakeModule().init(); + BatchExtensionInstaller installer = new BatchExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7")); + + installer.install(module); + } + + public static class FakeModule extends Module { + @Override + protected void configure() { + } + } + + @InstanciationStrategy(InstanciationStrategy.PER_BATCH) + public static class BatchService implements BatchExtension { + + } + + public static class ProjectService implements BatchExtension { + + } + + public static class ServerService implements ServerExtension { + + } + + @InstanciationStrategy(InstanciationStrategy.PER_BATCH) + public static class BatchServiceProvider extends ExtensionProvider implements BatchExtension { + + @Override + public Object provide() { + return Arrays.asList(BatchService.class, ServerService.class); + } + } + + public static class ProjectServiceProvider extends ExtensionProvider implements BatchExtension { + @Override + public Object provide() { + return ProjectService.class; + } + } + + @InstanciationStrategy(InstanciationStrategy.PER_BATCH) + public static class InvalidCoverageExtension implements CoverageExtension { + // strategy PER_BATCH is not allowed + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchModuleTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchModuleTest.java new file mode 100644 index 00000000000..6b6c14a65cd --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchModuleTest.java @@ -0,0 +1,27 @@ +/* + * 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.bootstrap; + +import org.junit.Ignore; + +@Ignore +public class BatchModuleTest { + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java index f134ad425c6..18a6281e6b8 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertThat; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.PropertiesConfiguration; +import org.junit.Ignore; import org.junit.Test; import org.picocontainer.MutablePicoContainer; import org.sonar.api.BatchExtension; @@ -34,67 +35,68 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Project.AnalysisType; import org.sonar.api.utils.IocContainer; +@Ignore public class BatchPluginRepositoryTest { - @Test - public void shouldRegisterBatchExtension() { - MutablePicoContainer pico = IocContainer.buildPicoContainer(); - pico.addComponent(new PropertiesConfiguration()); - BatchPluginRepository repository = new BatchPluginRepository(); - - // check classes - assertThat(repository.shouldRegisterExtension(pico, "foo", FakeBatchExtension.class), is(true)); - assertThat(repository.shouldRegisterExtension(pico, "foo", FakeServerExtension.class), is(false)); - assertThat(repository.shouldRegisterExtension(pico, "foo", String.class), is(false)); - - // check objects - assertThat(repository.shouldRegisterExtension(pico, "foo", new FakeBatchExtension()), is(true)); - assertThat(repository.shouldRegisterExtension(pico, "foo", new FakeServerExtension()), is(false)); - assertThat(repository.shouldRegisterExtension(pico, "foo", "bar"), is(false)); - } - - @Test - public void shouldRegisterOnlyCoberturaExtensionByDefault() { - BatchPluginRepository repository = new BatchPluginRepository(); - PropertiesConfiguration conf = new PropertiesConfiguration(); - assertThat(repository.shouldRegisterCoverageExtension("cobertura", newJavaProject(), conf), is(true)); - assertThat(repository.shouldRegisterCoverageExtension("clover", newJavaProject(), conf), is(false)); - } - - @Test - public void shouldRegisterCustomCoverageExtension() { - Configuration conf = new PropertiesConfiguration(); - conf.setProperty(AbstractCoverageExtension.PARAM_PLUGIN, "clover,phpunit"); - BatchPluginRepository repository = new BatchPluginRepository(); - assertThat(repository.shouldRegisterCoverageExtension("cobertura", newJavaProject(), conf), is(false)); - assertThat(repository.shouldRegisterCoverageExtension("clover", newJavaProject(), conf), is(true)); - assertThat(repository.shouldRegisterCoverageExtension("phpunit", newJavaProject(), conf), is(true)); - assertThat(repository.shouldRegisterCoverageExtension("other", newJavaProject(), conf), is(false)); - } - - @Test - public void shouldNotCheckCoverageExtensionsOnNonJavaProjects() { - Configuration conf = new PropertiesConfiguration(); - conf.setProperty(AbstractCoverageExtension.PARAM_PLUGIN, "cobertura"); - BatchPluginRepository repository = new BatchPluginRepository(); - - assertThat(repository.shouldRegisterCoverageExtension("groovy", newGroovyProject(), conf), is(true)); - assertThat(repository.shouldRegisterCoverageExtension("groovy", newJavaProject(), conf), is(false)); - } - - private static Project newJavaProject() { - return new Project("foo").setLanguageKey(Java.KEY).setAnalysisType(AnalysisType.DYNAMIC); - } - - private static Project newGroovyProject() { - return new Project("foo").setLanguageKey("grvy").setAnalysisType(AnalysisType.DYNAMIC); - } - - public static class FakeBatchExtension implements BatchExtension { - - } - - public static class FakeServerExtension implements ServerExtension { - - } +// @Test +// public void shouldRegisterBatchExtension() { +// MutablePicoContainer pico = IocContainer.buildPicoContainer(); +// pico.addComponent(new PropertiesConfiguration()); +// BatchPluginRepository repository = new BatchPluginRepository(); +// +// // check classes +// assertThat(repository.shouldRegisterExtension(pico, "foo", FakeBatchExtension.class), is(true)); +// assertThat(repository.shouldRegisterExtension(pico, "foo", FakeServerExtension.class), is(false)); +// assertThat(repository.shouldRegisterExtension(pico, "foo", String.class), is(false)); +// +// // check objects +// assertThat(repository.shouldRegisterExtension(pico, "foo", new FakeBatchExtension()), is(true)); +// assertThat(repository.shouldRegisterExtension(pico, "foo", new FakeServerExtension()), is(false)); +// assertThat(repository.shouldRegisterExtension(pico, "foo", "bar"), is(false)); +// } +// +// @Test +// public void shouldRegisterOnlyCoberturaExtensionByDefault() { +// BatchPluginRepository repository = new BatchPluginRepository(); +// PropertiesConfiguration conf = new PropertiesConfiguration(); +// assertThat(repository.shouldRegisterCoverageExtension("cobertura", newJavaProject(), conf), is(true)); +// assertThat(repository.shouldRegisterCoverageExtension("clover", newJavaProject(), conf), is(false)); +// } +// +// @Test +// public void shouldRegisterCustomCoverageExtension() { +// Configuration conf = new PropertiesConfiguration(); +// conf.setProperty(AbstractCoverageExtension.PARAM_PLUGIN, "clover,phpunit"); +// BatchPluginRepository repository = new BatchPluginRepository(); +// assertThat(repository.shouldRegisterCoverageExtension("cobertura", newJavaProject(), conf), is(false)); +// assertThat(repository.shouldRegisterCoverageExtension("clover", newJavaProject(), conf), is(true)); +// assertThat(repository.shouldRegisterCoverageExtension("phpunit", newJavaProject(), conf), is(true)); +// assertThat(repository.shouldRegisterCoverageExtension("other", newJavaProject(), conf), is(false)); +// } +// +// @Test +// public void shouldNotCheckCoverageExtensionsOnNonJavaProjects() { +// Configuration conf = new PropertiesConfiguration(); +// conf.setProperty(AbstractCoverageExtension.PARAM_PLUGIN, "cobertura"); +// BatchPluginRepository repository = new BatchPluginRepository(); +// +// assertThat(repository.shouldRegisterCoverageExtension("groovy", newGroovyProject(), conf), is(true)); +// assertThat(repository.shouldRegisterCoverageExtension("groovy", newJavaProject(), conf), is(false)); +// } +// +// private static Project newJavaProject() { +// return new Project("foo").setLanguageKey(Java.KEY).setAnalysisType(AnalysisType.DYNAMIC); +// } +// +// private static Project newGroovyProject() { +// return new Project("foo").setLanguageKey("grvy").setAnalysisType(AnalysisType.DYNAMIC); +// } +// +// public static class FakeBatchExtension implements BatchExtension { +// +// } +// +// public static class FakeServerExtension implements ServerExtension { +// +// } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapModuleTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapModuleTest.java new file mode 100644 index 00000000000..f765a80080a --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapModuleTest.java @@ -0,0 +1,49 @@ +/* + * 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.bootstrap; + +import org.junit.Test; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Project; +import org.sonar.batch.MavenPluginExecutor; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class BootstrapModuleTest { + + class MyMavenPluginExecutor implements MavenPluginExecutor { + public void execute(Project project, String goal) { + } + + public MavenPluginHandler execute(Project project, MavenPluginHandler handler) { + return handler; + } + } + + @Test + public void shouldSearchMavenPluginExecutor() { + BootstrapModule module = new BootstrapModule(null, MyMavenPluginExecutor.class); + assertThat(module.isMavenPluginExecutorRegistered(), is(true)); + + module = new BootstrapModule(null); + assertThat(module.isMavenPluginExecutorRegistered(), is(false)); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java new file mode 100644 index 00000000000..b29fd6831f3 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionUtilsTest.java @@ -0,0 +1,99 @@ +/* + * 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.bootstrap; + +import org.junit.Test; +import org.sonar.api.BatchExtension; +import org.sonar.api.ServerExtension; +import org.sonar.api.batch.InstanciationStrategy; +import org.sonar.api.batch.SupportedEnvironment; +import org.sonar.batch.bootstrapper.EnvironmentInformation; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class ExtensionUtilsTest { + + @Test + public void shouldBeBatchInstantiationStrategy() { + assertThat(ExtensionUtils.isInstantiationStrategy(BatchService.class, InstanciationStrategy.PER_BATCH), is(true)); + assertThat(ExtensionUtils.isInstantiationStrategy(new BatchService(), InstanciationStrategy.PER_BATCH), is(true)); + assertThat(ExtensionUtils.isInstantiationStrategy(ProjectService.class, InstanciationStrategy.PER_BATCH), is(false)); + assertThat(ExtensionUtils.isInstantiationStrategy(new ProjectService(), InstanciationStrategy.PER_BATCH), is(false)); + assertThat(ExtensionUtils.isInstantiationStrategy(DefaultService.class, InstanciationStrategy.PER_BATCH), is(false)); + assertThat(ExtensionUtils.isInstantiationStrategy(new DefaultService(), InstanciationStrategy.PER_BATCH), is(false)); + } + + @Test + public void shouldBeProjectInstantiationStrategy() { + assertThat(ExtensionUtils.isInstantiationStrategy(BatchService.class, InstanciationStrategy.PER_PROJECT), is(false)); + assertThat(ExtensionUtils.isInstantiationStrategy(new BatchService(), InstanciationStrategy.PER_PROJECT), is(false)); + assertThat(ExtensionUtils.isInstantiationStrategy(ProjectService.class, InstanciationStrategy.PER_PROJECT), is(true)); + assertThat(ExtensionUtils.isInstantiationStrategy(new ProjectService(), InstanciationStrategy.PER_PROJECT), is(true)); + assertThat(ExtensionUtils.isInstantiationStrategy(DefaultService.class, InstanciationStrategy.PER_PROJECT), is(true)); + assertThat(ExtensionUtils.isInstantiationStrategy(new DefaultService(), InstanciationStrategy.PER_PROJECT), is(true)); + } + + @Test + public void testIsBatchExtension() { + assertThat(ExtensionUtils.isBatchExtension(BatchService.class), is(true)); + assertThat(ExtensionUtils.isBatchExtension(new BatchService()), is(true)); + + assertThat(ExtensionUtils.isBatchExtension(ServerService.class), is(false)); + assertThat(ExtensionUtils.isBatchExtension(new ServerService()), is(false)); + } + + @Test + public void shouldCheckEnvironment() { + assertThat(ExtensionUtils.isSupportedEnvironment(new MavenService(), new EnvironmentInformation("maven", "2.2.1")), is(true)); + assertThat(ExtensionUtils.isSupportedEnvironment(new BuildToolService(), new EnvironmentInformation("maven", "2.2.1")), is(true)); + assertThat(ExtensionUtils.isSupportedEnvironment(new DefaultService(), new EnvironmentInformation("maven", "2.2.1")), is(true)); + + assertThat(ExtensionUtils.isSupportedEnvironment(new BuildToolService(), new EnvironmentInformation("eclipse", "0.1")), is(false)); + } + + @InstanciationStrategy(InstanciationStrategy.PER_BATCH) + public static class BatchService implements BatchExtension { + + } + + @InstanciationStrategy(InstanciationStrategy.PER_PROJECT) + public static class ProjectService implements BatchExtension { + + } + + public static class DefaultService implements BatchExtension { + + } + + public static class ServerService implements ServerExtension { + + } + + @SupportedEnvironment("maven") + public static class MavenService implements BatchExtension { + + } + + @SupportedEnvironment({"maven", "ant", "gradle"}) + public static class BuildToolService implements BatchExtension { + + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ModuleTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ModuleTest.java new file mode 100644 index 00000000000..e85a72ecfad --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ModuleTest.java @@ -0,0 +1,154 @@ +/* + * 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.bootstrap; + +import org.hamcrest.Matchers; +import org.junit.Test; +import org.sonar.batch.bootstrap.Module; + +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class ModuleTest { + + @Test + public void shouldInitModule() { + Module module = new FakeModule(FakeService.class).init(); + + FakeService service = module.getComponent(FakeService.class); + assertThat(service, not(nullValue())); + assertThat(service.started, is(false)); + assertThat(module.getContainer(), not(nullValue())); + } + + @Test + public void shouldStartAndStopModule() { + Module module = new FakeModule(FakeService.class).init(); + module.start(); + + FakeService service = module.getComponent(FakeService.class); + assertThat(service.started, is(true)); + + module.stop(); + assertThat(service.started, is(false)); + } + + @Test(expected = RuntimeException.class) + public void shouldNotIgnoreStartFailures() { + Module module = new FakeModule(NonStartableService.class).init(); + module.start(); + } + + @Test + public void shouldIgnoreStopFailures() { + Module module = new FakeModule(NonStoppableService.class).init(); + module.start(); + module.stop(); // no exception is raised + } + + @Test + public void componentsShouldBeSingletons() { + Module module = new FakeModule(FakeService.class).init(); + + assertThat(module.getComponent(FakeService.class)==module.getComponent(FakeService.class), is(true)); + } + + @Test + public void shouldInstallChildModule() { + Module parent = new FakeModule(FakeService.class).init(); + parent.start(); + + Module child = parent.installChild(new FakeModule(ChildService.class)); + + assertThat(parent.getComponent(ChildService.class), Matchers.nullValue());// child not accessible from parent + assertThat(child.getComponent(FakeService.class), not(nullValue())); + assertThat(child.getComponent(ChildService.class).started, is(false)); + assertThat(child.getComponent(ChildService.class).dependency, not(nullValue())); + + child.start(); + assertThat(child.getComponent(ChildService.class).started, is(true)); + + child.stop(); + assertThat(child.getComponent(ChildService.class).started, is(false)); + } + + public static class FakeModule extends Module { + private Class[] components; + + public FakeModule(Class... components) { + this.components = components; + } + + @Override + protected void configure() { + for (Class component : components) { + addComponent(component); + } + } + + public boolean equals(Object obj) { + return false; + } + } + + public static class FakeService { + boolean started = false; + + public void start() { + this.started = true; + } + + public void stop() { + this.started = false; + } + } + + public static class ChildService { + private FakeService dependency; + private boolean started = false; + + public ChildService(FakeService dependency) { + this.dependency = dependency; + } + + public void start() { + this.started = true; + } + + public void stop() { + this.started = false; + } + } + + public static class NonStoppableService { + public void stop() { + throw new IllegalStateException("Can not stop !"); + } + } + + public static class NonStartableService { + public void start() { + throw new IllegalStateException("Can not start !"); + } + } + +} |