Browse Source

SONAR-4069 new task extension point

tags/3.6
Simon Brandhof 11 years ago
parent
commit
273fa31d92
40 changed files with 838 additions and 1335 deletions
  1. 29
    38
      sonar-batch/src/main/java/org/sonar/batch/Batch.java
  2. 12
    19
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchSettings.java
  3. 109
    69
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
  4. 6
    12
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapProperties.java
  5. 11
    13
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapSettings.java
  6. 10
    5
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/DryRunDatabase.java
  7. 29
    145
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
  8. 7
    17
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionUtils.java
  9. 2
    6
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/MetricProvider.java
  10. 7
    6
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
  11. 2
    1
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerMetadata.java
  12. 0
    79
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskBootstrapContainer.java
  13. 30
    152
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
  14. 25
    14
      sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
  15. 7
    7
      sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java
  16. 149
    0
      sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
  17. 136
    0
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
  18. 0
    149
      sonar-batch/src/main/java/org/sonar/batch/scan/ScanContainer.java
  19. 9
    29
      sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java
  20. 8
    12
      sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java
  21. 0
    78
      sonar-batch/src/test/java/org/sonar/batch/BatchTest.java
  22. 23
    21
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchSettingsTest.java
  23. 18
    46
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapContainerTest.java
  24. 20
    16
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapPropertiesTest.java
  25. 31
    8
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapSettingsTest.java
  26. 10
    4
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/DryRunDatabaseTest.java
  27. 48
    103
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
  28. 5
    11
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
  29. 0
    56
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/TaskBootstrapContainerTest.java
  30. 0
    65
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/TaskContainerTest.java
  31. 2
    7
      sonar-batch/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java
  32. 0
    72
      sonar-batch/src/test/java/org/sonar/batch/scan/ScanContainerTest.java
  33. 0
    59
      sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java
  34. 4
    2
      sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java
  35. 2
    4
      sonar-plugin-api/src/main/java/org/sonar/api/measures/Metrics.java
  36. 33
    2
      sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java
  37. 2
    2
      sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypeTree.java
  38. 2
    1
      sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypes.java
  39. 42
    1
      sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java
  40. 8
    4
      sonar-server/src/main/webapp/WEB-INF/app/controllers/batch_bootstrap_controller.rb

+ 29
- 38
sonar-batch/src/main/java/org/sonar/batch/Batch.java View File

@@ -22,56 +22,38 @@ package org.sonar.batch;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.batch.bootstrap.BootstrapContainer;
import org.sonar.batch.bootstrap.Container;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.bootstrapper.Reactor;

import java.util.Iterator;
import java.util.Properties;

/**
* @deprecated Replaced by {@link org.sonar.batch.bootstrapper.Batch} since version 2.14.
* @deprecated in 2.14. Replaced by {@link org.sonar.batch.bootstrapper.Batch}.
*/
@Deprecated
public final class Batch {

private Container bootstrapModule;
private Object[] bootstrapExtensions;
private ProjectReactor reactor;

public Batch(ProjectReactor reactor, Object... bootstrapperComponents) {
this.bootstrapModule = new BootstrapContainer(reactor, bootstrapperComponents);
this.bootstrapModule.init();
this.reactor = reactor;
this.bootstrapExtensions = bootstrapperComponents;
}

/**
* @deprecated since 2.9 because commons-configuration is replaced by ProjectDefinition#properties. Used by Ant Task 1.1
*/
@Deprecated
public Batch(Configuration configuration, Object... bootstrapperComponents) {//NOSONAR configuration is not needed
public Batch(Configuration configuration, Object... bootstrapperComponents) {
// configuration is not needed
// because it's already included in ProjectDefinition.
this.bootstrapModule = new BootstrapContainer(extractProjectReactor(bootstrapperComponents), bootstrapperComponents);
this.bootstrapModule.init();
}

static ProjectReactor extractProjectReactor(Object[] components) {
Reactor deprecatedReactor = null;
for (Object component : components) {
if (component instanceof ProjectReactor) {
return (ProjectReactor) component;
}
if (component instanceof Reactor) {
deprecatedReactor = (Reactor) component;
}
}

if (deprecatedReactor == null) {
throw new IllegalArgumentException("Project reactor is not defined");
}
return deprecatedReactor.toProjectReactor();
this.bootstrapExtensions = bootstrapperComponents;
this.reactor = extractProjectReactor(bootstrapperComponents);
}

/**
* Used by Gradle 1.0
*
* @deprecated in version 2.12
*/
@Deprecated
@@ -94,18 +76,27 @@ public final class Batch {
return props;
}

/**
* for unit tests
*/
Batch(Container bootstrapModule) {
this.bootstrapModule = bootstrapModule;
static ProjectReactor extractProjectReactor(Object[] components) {
Reactor deprecatedReactor = null;
for (Object component : components) {
if (component instanceof ProjectReactor) {
return (ProjectReactor) component;
}
if (component instanceof Reactor) {
deprecatedReactor = (Reactor) component;
}
}

if (deprecatedReactor == null) {
throw new IllegalArgumentException("Project reactor is not defined");
}
return deprecatedReactor.toProjectReactor();
}

public void execute() {
try {
bootstrapModule.start();
} finally {
bootstrapModule.stop();
}
org.sonar.batch.bootstrapper.Batch.Builder builder = org.sonar.batch.bootstrapper.Batch.builder();
builder.setProjectReactor(reactor);
builder.addComponents(bootstrapExtensions);
builder.build().execute();
}
}

+ 12
- 19
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchSettings.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.batch.bootstrap;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
@@ -43,39 +42,34 @@ public class BatchSettings extends Settings {
private Map<String, Map<String, String>> moduleProperties = Maps.newHashMap();

public BatchSettings(BootstrapSettings bootstrapSettings, PropertyDefinitions propertyDefinitions,
ServerClient client, Configuration deprecatedConfiguration, GlobalBatchProperties globalProperties) {
this(bootstrapSettings, propertyDefinitions, null, client, deprecatedConfiguration, globalProperties);
ServerClient client, Configuration deprecatedConfiguration, BootstrapProperties globalProperties) {
this(bootstrapSettings, propertyDefinitions, client, deprecatedConfiguration, globalProperties, null);
}

public BatchSettings(BootstrapSettings bootstrapSettings, PropertyDefinitions propertyDefinitions, @Nullable ProjectReactor reactor,
ServerClient client, Configuration deprecatedConfiguration, GlobalBatchProperties globalProperties) {
public BatchSettings(BootstrapSettings bootstrapSettings, PropertyDefinitions propertyDefinitions,
ServerClient client, Configuration deprecatedConfiguration, BootstrapProperties bootstrapProperties,
@Nullable ProjectReactor projectReactor) {
super(propertyDefinitions);
this.deprecatedConfiguration = deprecatedConfiguration;
init(bootstrapSettings, reactor, client, globalProperties);
}

@VisibleForTesting
public BatchSettings() {
init(bootstrapSettings, client, bootstrapProperties, projectReactor);
}

private void init(BootstrapSettings bootstrapSettings, @Nullable ProjectReactor reactor, ServerClient client,
GlobalBatchProperties globalProperties) {
private void init(BootstrapSettings bootstrapSettings, ServerClient client, BootstrapProperties bootstrapProperties, @Nullable ProjectReactor reactor) {
LoggerFactory.getLogger(BatchSettings.class).info("Load batch settings");

if (reactor != null) {
String branch = bootstrapSettings.getProperty(CoreProperties.PROJECT_BRANCH_PROPERTY);
String branch = bootstrapSettings.property(CoreProperties.PROJECT_BRANCH_PROPERTY);
String projectKey = reactor.getRoot().getKey();
if (StringUtils.isNotBlank(branch)) {
projectKey = String.format("%s:%s", projectKey, branch);
}
downloadSettings(client, projectKey);
}
else {
} else {
downloadSettings(client, null);
}

// order is important -> bottom-up. The last one overrides all the others.
addProperties(globalProperties.getProperties());
addProperties(bootstrapProperties.properties());
if (reactor != null) {
addProperties(reactor.getRoot().getProperties());
}
@@ -87,8 +81,7 @@ public class BatchSettings extends Settings {
String url;
if (StringUtils.isNotBlank(projectKey)) {
url = "/batch_bootstrap/properties?project=" + projectKey;
}
else {
} else {
url = "/batch_bootstrap/properties";
}
String jsonText = client.request(url);
@@ -97,7 +90,7 @@ public class BatchSettings extends Settings {
String key = jsonProperty.get("k");
String value = jsonProperty.get("v");
String moduleKey = jsonProperty.get("p");
if (moduleKey == null || projectKey.equals(moduleKey)) {
if (moduleKey == null || projectKey == null || moduleKey.equals(projectKey)) {
setProperty(key, value);
}
if (moduleKey != null) {

+ 109
- 69
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java View File

@@ -20,91 +20,131 @@
package org.sonar.batch.bootstrap;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.Plugin;
import org.sonar.api.config.EmailSettings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.utils.HttpDownloader;
import org.sonar.api.utils.UriReader;
import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
import org.sonar.batch.scan.maven.MavenPluginExecutor;
import org.sonar.batch.components.PastMeasuresLoader;
import org.sonar.batch.components.PastSnapshotFinder;
import org.sonar.batch.components.PastSnapshotFinderByDate;
import org.sonar.batch.components.PastSnapshotFinderByDays;
import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis;
import org.sonar.batch.components.PastSnapshotFinderByPreviousVersion;
import org.sonar.batch.components.PastSnapshotFinderByVersion;
import org.sonar.core.config.Logback;
import org.sonar.core.i18n.I18nManager;
import org.sonar.core.i18n.RuleI18nManager;
import org.sonar.core.metric.CacheMetricFinder;
import org.sonar.core.persistence.DaoUtils;
import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.persistence.SemaphoreUpdater;
import org.sonar.core.persistence.SemaphoresImpl;
import org.sonar.core.qualitymodel.DefaultModelFinder;
import org.sonar.core.rule.CacheRuleFinder;
import org.sonar.core.user.DefaultUserFinder;
import org.sonar.jpa.dao.MeasuresDao;
import org.sonar.jpa.dao.ProfilesDao;
import org.sonar.jpa.dao.RulesDao;
import org.sonar.jpa.session.DefaultDatabaseConnector;
import org.sonar.jpa.session.JpaDatabaseSession;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;

/**
* Level 1 components
*/
public class BootstrapContainer extends Container {
public class BootstrapContainer extends ComponentContainer {

private Object[] boostrapperComponents;
private ProjectReactor reactor;
private GlobalBatchProperties globalProperties;
private String taskCommand;
private BootstrapContainer() {
super();
}

/**
* @deprecated Use {@link #BootstrapModule(GlobalBatchProperties, String, ProjectReactor, Object...)}
*/
@Deprecated
public BootstrapContainer(ProjectReactor reactor, Object... boostrapperComponents) {
this(new GlobalBatchProperties(), null, reactor, boostrapperComponents);
@Override
protected void doBeforeStart() {
addBootstrapComponents();
addDatabaseComponents();
addCoreComponents();
}

public BootstrapContainer(GlobalBatchProperties globalProperties, @Nullable String taskCommand, @Nullable ProjectReactor reactor,
Object... boostrapperComponents) {
this.globalProperties = globalProperties;
this.taskCommand = taskCommand;
this.reactor = reactor;
this.boostrapperComponents = boostrapperComponents;
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()
);
}

@Override
protected void configure() {
container.addSingleton(globalProperties);
if (reactor != null) {
container.addSingleton(reactor);
}
container.addSingleton(new PropertiesConfiguration());
container.addSingleton(BootstrapSettings.class);
container.addSingleton(ServerClient.class);
container.addSingleton(BatchSettings.class);
container.addSingleton(BatchPluginRepository.class);
container.addSingleton(ExtensionInstaller.class);
container.addSingleton(Logback.class);
container.addSingleton(ServerMetadata.class);
container.addSingleton(org.sonar.batch.ServerMetadata.class);
container.addSingleton(TempDirectories.class);
container.addSingleton(HttpDownloader.class);
container.addSingleton(UriReader.class);
container.addSingleton(PluginDownloader.class);
container.addPicoAdapter(new FileCacheProvider());
for (Object component : boostrapperComponents) {
if (component != null) {
container.addSingleton(component);
}
}
if (!isMavenPluginExecutorRegistered()) {
container.addSingleton(FakeMavenPluginExecutor.class);
}
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()
);
}

boolean isMavenPluginExecutorRegistered() {
if (boostrapperComponents != null) {
for (Object component : boostrapperComponents) {
if (component instanceof Class && MavenPluginExecutor.class.isAssignableFrom((Class<?>) component)) {
return true;
}
}
}
return false;
/**
* These components MUST not depend on extensions provided by plugins
*/
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
);
}

@Override
protected void doStart() {
Container childModule = new TaskBootstrapContainer(taskCommand);
try {
installChild(childModule);
childModule.start();
} finally {
childModule.stop();
uninstallChild();
protected void doAfterStart() {
installPlugins();
}

private void installPlugins() {
for (Map.Entry<PluginMetadata, Plugin> entry : get(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;
}
}

sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalBatchProperties.java → sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapProperties.java View File

@@ -19,29 +19,23 @@
*/
package org.sonar.batch.bootstrap;

import com.google.common.collect.Maps;
import com.google.common.collect.ImmutableMap;

import java.util.Map;

/**
* Batch properties that are not specific to a project (for example
* coming from global configuration file of sonar-runner).
* @author Julien HENRY
*
*/
public class GlobalBatchProperties {

protected final Map<String, String> properties;
public class BootstrapProperties {

public GlobalBatchProperties() {
this.properties = Maps.newHashMap();
}
private final Map<String, String> properties;

public GlobalBatchProperties(Map<String, String> properties) {
this.properties = Maps.newHashMap(properties);
public BootstrapProperties(Map<String, String> properties) {
this.properties = ImmutableMap.copyOf(properties);
}

public Map<String, String> getProperties() {
Map<String, String> properties() {
return properties;
}


+ 11
- 13
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapSettings.java View File

@@ -23,6 +23,8 @@ import com.google.common.collect.Maps;
import org.codehaus.plexus.util.StringUtils;
import org.sonar.api.batch.bootstrap.ProjectReactor;

import javax.annotation.Nullable;

import java.util.Map;
import java.util.Properties;

@@ -32,21 +34,17 @@ import java.util.Properties;
public class BootstrapSettings {
private Map<String, String> properties;

public BootstrapSettings(GlobalBatchProperties globalProperties) {
init(null, globalProperties);
}

public BootstrapSettings(ProjectReactor reactor, GlobalBatchProperties globalProperties) {
init(reactor, globalProperties);
public BootstrapSettings(BootstrapProperties bootstrapProperties) {
this(bootstrapProperties, null);
}

private void init(ProjectReactor reactor, GlobalBatchProperties globalProperties) {
public BootstrapSettings(BootstrapProperties bootstrapProperties, @Nullable ProjectReactor projectReactor) {
properties = Maps.newHashMap();

// order is important -> bottom-up. The last one overrides all the others.
properties.putAll(globalProperties.getProperties());
if (reactor != null) {
addProperties(reactor.getRoot().getProperties());
properties.putAll(bootstrapProperties.properties());
if (projectReactor != null) {
addProperties(projectReactor.getRoot().getProperties());
}
properties.putAll(System.getenv());
addProperties(System.getProperties());
@@ -60,15 +58,15 @@ public class BootstrapSettings {
}
}

public Map<String, String> getProperties() {
public Map<String, String> properties() {
return properties;
}

public String getProperty(String key) {
public String property(String key) {
return properties.get(key);
}

public String getProperty(String key, String defaultValue) {
public String property(String key, String defaultValue) {
return StringUtils.defaultString(properties.get(key), defaultValue);
}
}

+ 10
- 5
sonar-batch/src/main/java/org/sonar/batch/bootstrap/DryRunDatabase.java View File

@@ -31,6 +31,8 @@ import org.sonar.api.config.Settings;
import org.sonar.api.database.DatabaseProperties;
import org.sonar.api.utils.SonarException;

import javax.annotation.Nullable;

import java.io.File;

import static org.sonar.api.utils.HttpDownloader.HttpException;
@@ -50,22 +52,25 @@ public class DryRunDatabase implements BatchComponent {
private final Settings settings;
private final ServerClient server;
private final TempDirectories tempDirectories;
private final ProjectReactor reactor;
private ProjectReactor reactor;

public DryRunDatabase(Settings settings, ServerClient server, TempDirectories tempDirectories, ProjectReactor reactor,
// project reactor must be completely built
ProjectReactorReady reactorReady) {
public DryRunDatabase(Settings settings, ServerClient server, TempDirectories tempDirectories, @Nullable ProjectReactor reactor) {
this.settings = settings;
this.server = server;
this.tempDirectories = tempDirectories;
this.reactor = reactor;
}

public DryRunDatabase(Settings settings, ServerClient server, TempDirectories tempDirectories) {
this(settings, server, tempDirectories, null);
}

public void start() {
if (settings.getBoolean(CoreProperties.DRY_RUN)) {
LOG.info("Dry run");
File databaseFile = tempDirectories.getFile("", "dryrun.h2.db");
downloadDatabase(reactor.getRoot().getKey(), databaseFile);
String projectKey = reactor != null ? StringUtils.defaultString(reactor.getRoot().getKey()) : "";
downloadDatabase(projectKey, databaseFile);

String databasePath = StringUtils.removeEnd(databaseFile.getAbsolutePath(), ".h2.db");
replaceSettings(databasePath);

+ 29
- 145
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java View File

@@ -19,19 +19,12 @@
*/
package org.sonar.batch.bootstrap;

import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
import org.sonar.api.CoreProperties;
import org.sonar.api.ExtensionProvider;
import org.sonar.api.Plugin;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.resources.Project;
import org.sonar.api.task.TaskDefinition;
import org.sonar.batch.bootstrapper.EnvironmentInformation;

import javax.annotation.Nullable;
@@ -39,167 +32,58 @@ import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;

public class ExtensionInstaller implements BatchComponent {
// TODO rename ExtensionInstaller
public class ExtensionInstaller {

private static final Logger LOG = LoggerFactory.getLogger(ExtensionInstaller.class);
private final BatchPluginRepository pluginRepository;
private final EnvironmentInformation env;
private final Settings settings;

private BatchPluginRepository pluginRepository;
private EnvironmentInformation environment;
private Settings settings;

public ExtensionInstaller(BatchPluginRepository pluginRepository, EnvironmentInformation environment, Settings settings) {
public ExtensionInstaller(BatchPluginRepository pluginRepository, EnvironmentInformation env, Settings settings) {
this.pluginRepository = pluginRepository;
this.environment = environment;
this.env = env;
this.settings = settings;
}

public void installTaskDefinitionExtensions(ComponentContainer container) {
for (Map.Entry<PluginMetadata, Plugin> entry : pluginRepository.getPluginsByMetadata().entrySet()) {
PluginMetadata metadata = entry.getKey();
Plugin plugin = entry.getValue();

container.addExtension(metadata, plugin);
for (Object extension : plugin.getExtensions()) {
installTaskDefinition(container, metadata, extension);
}
}
}

boolean installTaskDefinition(ComponentContainer container, @Nullable PluginMetadata plugin, Object extension) {
boolean installed;
if (ExtensionUtils.isType(extension, TaskDefinition.class)
&& ExtensionUtils.supportsEnvironment(extension, environment)) {
LOG.debug("Installing task definition extension {} from plugin {}", extension.toString(), plugin.getKey());
container.addExtension(plugin, extension);
installed = true;
} else {
container.declareExtension(plugin, extension);
installed = false;
}
return installed;
}

public void installTaskExtensions(ComponentContainer container, boolean projectPresent) {
boolean dryRun = settings.getBoolean(CoreProperties.DRY_RUN);
public ExtensionInstaller install(ComponentContainer container, ComponentFilter matcher) {
boolean dryRun = isDryRun();
for (Map.Entry<PluginMetadata, Plugin> entry : pluginRepository.getPluginsByMetadata().entrySet()) {
PluginMetadata metadata = entry.getKey();
Plugin plugin = entry.getValue();

container.addExtension(metadata, plugin);
for (Object extension : plugin.getExtensions()) {
installTaskExtension(container, metadata, extension, projectPresent);
if (projectPresent) {
installBatchExtension(container, metadata, extension, dryRun, InstantiationStrategy.PER_BATCH);
}
doInstall(container, matcher, metadata, dryRun, extension);
}
}

List<ExtensionProvider> providers = container.getComponentsByType(ExtensionProvider.class);
for (ExtensionProvider provider : providers) {
executeTaskExtensionProvider(container, provider, projectPresent);
if (projectPresent) {
executeBatchExtensionProvider(container, InstantiationStrategy.PER_BATCH, dryRun, provider);
}
}
}

private void executeTaskExtensionProvider(ComponentContainer container, ExtensionProvider provider, boolean projectPresent) {
Object obj = provider.provide();
if (obj instanceof Iterable) {
for (Object extension : (Iterable) obj) {
installTaskExtension(container, null, extension, projectPresent);
}
} else {
installTaskExtension(container, null, obj, projectPresent);
}
}

boolean installTaskExtension(ComponentContainer container, @Nullable PluginMetadata plugin, Object extension, boolean projectPresent) {
boolean installed;
if (ExtensionUtils.isTaskExtension(extension) &&
(projectPresent || !ExtensionUtils.requiresProject(extension)) &&
ExtensionUtils.supportsEnvironment(extension, environment)) {
logInstallExtension("task", plugin, extension);
container.addExtension(plugin, extension);
installed = true;
} else {
container.declareExtension(plugin, extension);
installed = false;
}
return installed;
}

public void installInspectionExtensions(ComponentContainer container) {
installBatchExtensions(container, InstantiationStrategy.PER_PROJECT);
}

@VisibleForTesting
void installBatchExtensions(ComponentContainer container, String instantiationStrategy) {
boolean dryRun = settings.getBoolean(CoreProperties.DRY_RUN);
for (Map.Entry<PluginMetadata, Plugin> entry : pluginRepository.getPluginsByMetadata().entrySet()) {
PluginMetadata metadata = entry.getKey();
Plugin plugin = entry.getValue();

container.addExtension(metadata, plugin);
for (Object extension : plugin.getExtensions()) {
installBatchExtension(container, metadata, extension, dryRun, instantiationStrategy);
}
}

List<ExtensionProvider> providers = container.getComponentsByType(ExtensionProvider.class);
for (ExtensionProvider provider : providers) {
executeBatchExtensionProvider(container, instantiationStrategy, dryRun, provider);
}
}

private void executeBatchExtensionProvider(ComponentContainer container, String instantiationStrategy, boolean dryRun, ExtensionProvider provider) {
Object obj = provider.provide();
if (obj instanceof Iterable) {
for (Object extension : (Iterable) obj) {
installBatchExtension(container, null, extension, dryRun, instantiationStrategy);
Object object = provider.provide();
if (object instanceof Iterable) {
for (Object extension : (Iterable) object) {
doInstall(container, matcher, null, dryRun, extension);
}
} else {
doInstall(container, matcher, null, dryRun, object);
}
} else {
installBatchExtension(container, null, obj, dryRun, instantiationStrategy);
}
return this;
}

boolean installBatchExtension(ComponentContainer container, @Nullable PluginMetadata plugin, Object extension, boolean dryRun, String instantiationStrategy) {
boolean installed;
if (ExtensionUtils.isBatchExtension(extension) &&
ExtensionUtils.supportsEnvironment(extension, environment) &&
(!dryRun || ExtensionUtils.supportsDryRun(extension)) &&
ExtensionUtils.isInstantiationStrategy(extension, instantiationStrategy) &&
!isMavenExtensionOnEmulatedMavenProject(extension, instantiationStrategy, container)) {
logInstallExtension("batch", plugin, extension);
container.addExtension(plugin, extension);
installed = true;
private void doInstall(ComponentContainer container, ComponentFilter matcher, @Nullable PluginMetadata metadata, boolean dryRun, Object extension) {
if (ExtensionUtils.supportsEnvironment(extension, env)
&& (!dryRun || ExtensionUtils.supportsDryRun(extension))
&& matcher.accept(extension)) {
container.addExtension(metadata, extension);
} else {
container.declareExtension(plugin, extension);
installed = false;
container.declareExtension(metadata, extension);
}
return installed;
}

/**
* Special use-case: the extension point ProjectBuilder is used in a Maven environment to define some
* new sub-projects without pom.
* Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom.
*/
static boolean isMavenExtensionOnEmulatedMavenProject(Object extension, String instantiationStrategy, ComponentContainer container) {
if (InstantiationStrategy.PER_PROJECT.equals(instantiationStrategy) && ExtensionUtils.isMavenExtensionOnly(extension)) {
Project project = container.getComponentByType(Project.class);
return project != null && project.getPom() == null;
}
return false;
private boolean isDryRun() {
return settings.getBoolean(CoreProperties.DRY_RUN);
}

private void logInstallExtension(String extensionType, PluginMetadata plugin, Object extension) {
if (plugin != null) {
LOG.debug("Installing {} extension {} from plugin {}", new String[] {extensionType, extension.toString(), plugin.getKey()});
}
else {
LOG.debug("Installing {} extension {}", extensionType, extension.toString());
}
// TODO rename ExtensionMatcher
public interface ComponentFilter {
boolean accept(Object extension);
}

}

+ 7
- 17
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionUtils.java View File

@@ -23,19 +23,17 @@ import org.apache.commons.lang.StringUtils;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.SupportedEnvironment;
import org.sonar.api.task.TaskExtension;
import org.sonar.api.utils.AnnotationUtils;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.tasks.RequiresProject;
import org.sonar.core.DryRunIncompatible;

final class ExtensionUtils {
public class ExtensionUtils {

private ExtensionUtils() {
// only static methods
}

static boolean isInstantiationStrategy(Object extension, String strategy) {
public static boolean isInstantiationStrategy(Object extension, String strategy) {
InstantiationStrategy annotation = AnnotationUtils.getAnnotation(extension, InstantiationStrategy.class);
if (annotation != null) {
return strategy.equals(annotation.value());
@@ -43,15 +41,11 @@ final class ExtensionUtils {
return InstantiationStrategy.PER_PROJECT.equals(strategy);
}

static boolean isTaskExtension(Object extension) {
return isType(extension, TaskExtension.class);
}

static boolean isBatchExtension(Object extension) {
public static boolean isBatchExtension(Object extension) {
return isType(extension, BatchExtension.class);
}

static boolean supportsEnvironment(Object extension, EnvironmentInformation environment) {
public static boolean supportsEnvironment(Object extension, EnvironmentInformation environment) {
SupportedEnvironment env = AnnotationUtils.getAnnotation(extension, SupportedEnvironment.class);
if (env == null) {
return true;
@@ -64,20 +58,16 @@ final class ExtensionUtils {
return false;
}

static boolean supportsDryRun(Object extension) {
public static boolean supportsDryRun(Object extension) {
return AnnotationUtils.getAnnotation(extension, DryRunIncompatible.class) == null;
}

static boolean requiresProject(Object extension) {
return AnnotationUtils.getAnnotation(extension, RequiresProject.class) != null;
}

static boolean isMavenExtensionOnly(Object extension) {
public static boolean isMavenExtensionOnly(Object extension) {
SupportedEnvironment env = AnnotationUtils.getAnnotation(extension, SupportedEnvironment.class);
return env != null && env.value().length == 1 && StringUtils.equalsIgnoreCase("maven", env.value()[0]);
}

static boolean isType(Object extension, Class<?> extensionClass) {
public static boolean isType(Object extension, Class<?> extensionClass) {
Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass());
return extensionClass.isAssignableFrom(clazz);
}

+ 2
- 6
sonar-batch/src/main/java/org/sonar/batch/bootstrap/MetricProvider.java View File

@@ -20,18 +20,15 @@
package org.sonar.batch.bootstrap;

import com.google.common.collect.Lists;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.ExtensionProvider;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.Metrics;
import org.sonar.api.task.TaskComponent;

import java.util.List;

@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public class MetricProvider extends ExtensionProvider implements BatchExtension {
public class MetricProvider extends ExtensionProvider implements TaskComponent {

private Metrics[] factories;

@@ -45,7 +42,6 @@ public class MetricProvider extends ExtensionProvider implements BatchExtension

@Override
public List<Metric> provide() {
LoggerFactory.getLogger(MetricProvider.class).debug("Load metrics");
List<Metric> metrics = Lists.newArrayList(CoreMetrics.getMetrics());
for (Metrics factory : factories) {
metrics.addAll(factory.getMetrics());

+ 7
- 6
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java View File

@@ -38,6 +38,7 @@ import java.io.InputStream;
import java.net.URI;

/**
* Replace the deprecated org.sonar.batch.ServerMetadata
* TODO extends Server when removing the deprecated org.sonar.batch.ServerMetadata
*
* @since 3.4
@@ -48,11 +49,11 @@ public class ServerClient implements BatchComponent {

public ServerClient(BootstrapSettings settings, EnvironmentInformation env) {
this.settings = settings;
this.downloader = new HttpDownloader.BaseHttpDownloader(settings.getProperties(), env.toString());
this.downloader = new HttpDownloader.BaseHttpDownloader(settings.properties(), env.toString());
}

public String getURL() {
return StringUtils.removeEnd(settings.getProperty("sonar.host.url", "http://localhost:9000"), "/");
return StringUtils.removeEnd(settings.property("sonar.host.url", "http://localhost:9000"), "/");
}

public void download(String pathStartingWithSlash, File toFile) {
@@ -86,14 +87,14 @@ public class ServerClient implements BatchComponent {
String path = StringEscapeUtils.escapeHtml(pathStartingWithSlash);

URI uri = URI.create(getURL() + path);
String login = settings.getProperty(CoreProperties.LOGIN);
String login = settings.property(CoreProperties.LOGIN);

try {
InputSupplier<InputStream> inputSupplier;
if (Strings.isNullOrEmpty(login)) {
inputSupplier = downloader.newInputSupplier(uri);
} else {
inputSupplier = downloader.newInputSupplier(uri, login, settings.getProperty(CoreProperties.PASSWORD));
inputSupplier = downloader.newInputSupplier(uri, login, settings.property(CoreProperties.PASSWORD));
}
return inputSupplier;
} catch (Exception e) {
@@ -109,8 +110,8 @@ public class ServerClient implements BatchComponent {
}

private String getMessageWhenNotAuthorized() {
String login = settings.getProperty(CoreProperties.LOGIN);
String password = settings.getProperty(CoreProperties.PASSWORD);
String login = settings.property(CoreProperties.LOGIN);
String password = settings.property(CoreProperties.PASSWORD);
if (StringUtils.isEmpty(login) && StringUtils.isEmpty(password)) {
return "Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties %s and %s.";
}

+ 2
- 1
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerMetadata.java View File

@@ -32,8 +32,9 @@ import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @since 3.4. Replaces the deprecated org.sonar.batch.ServerMetadata
* @deprecated in 3.4. Replaced by {@link org.sonar.batch.bootstrap.ServerClient}
*/
@Deprecated
public class ServerMetadata extends Server implements BatchComponent {

private Settings settings;

+ 0
- 79
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskBootstrapContainer.java View File

@@ -1,79 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.batch.bootstrap.ProjectReactor;
import org.sonar.api.task.TaskDefinition;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.scan.ScanTask;
import org.sonar.batch.tasks.ListTasksTask;
import org.sonar.batch.tasks.Tasks;

import javax.annotation.Nullable;

/**
* Level-2 components. Collect tasks definitions.
*/
public class TaskBootstrapContainer extends Container {

private String taskCommand;

public TaskBootstrapContainer(@Nullable String taskCommand) {
this.taskCommand = taskCommand;
}

@Override
protected void configure() {
registerCoreTaskDefinitions();
registerTaskDefinitionExtensions();
container.addSingleton(Tasks.class);
}

private void registerCoreTaskDefinitions() {
container.addSingleton(ScanTask.DEFINITION);
container.addSingleton(ListTasksTask.DEFINITION);
}

private void registerTaskDefinitionExtensions() {
ExtensionInstaller installer = container.getComponentByType(ExtensionInstaller.class);
installer.installTaskDefinitionExtensions(container);
}

@Override
protected void doStart() {
Tasks tasks = container.getComponentByType(Tasks.class);
executeTask(tasks.getTaskDefinition(taskCommand));
}

private void executeTask(TaskDefinition taskDefinition) {
boolean projectPresent = container.getComponentByType(ProjectReactor.class) != null;
if (ExtensionUtils.requiresProject(taskDefinition.getTask()) && !projectPresent) {
throw new SonarException("Task '" + taskDefinition.getName() + "' requires to be run on a project");
}
Container childModule = new TaskContainer(taskDefinition, projectPresent);
try {
installChild(childModule);
childModule.start();
} finally {
childModule.stop();
uninstallChild();
}
}
}

+ 30
- 152
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java View File

@@ -19,176 +19,54 @@
*/
package org.sonar.batch.bootstrap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.config.EmailSettings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.ResourceTypes;
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.DefaultFileLinesContextFactory;
import org.sonar.batch.DefaultResourceCreationLock;
import org.sonar.batch.ProjectConfigurator;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.components.PastMeasuresLoader;
import org.sonar.batch.components.PastSnapshotFinder;
import org.sonar.batch.components.PastSnapshotFinderByDate;
import org.sonar.batch.components.PastSnapshotFinderByDays;
import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis;
import org.sonar.batch.components.PastSnapshotFinderByPreviousVersion;
import org.sonar.batch.components.PastSnapshotFinderByVersion;
import org.sonar.batch.index.DefaultIndex;
import org.sonar.batch.index.DefaultPersistenceManager;
import org.sonar.batch.index.DefaultResourcePersister;
import org.sonar.batch.index.DependencyPersister;
import org.sonar.batch.index.EventPersister;
import org.sonar.batch.index.LinkPersister;
import org.sonar.batch.index.MeasurePersister;
import org.sonar.batch.index.MemoryOptimizer;
import org.sonar.batch.index.SourcePersister;
import org.sonar.batch.scan.ScanTask;
import org.sonar.batch.tasks.ListTasksTask;
import org.sonar.core.component.ScanGraph;
import org.sonar.core.component.ScanGraphStore;
import org.sonar.core.component.ScanPerspectives;
import org.sonar.core.i18n.I18nManager;
import org.sonar.core.i18n.RuleI18nManager;
import org.sonar.core.metric.CacheMetricFinder;
import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.core.persistence.DaoUtils;
import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.persistence.SemaphoreUpdater;
import org.sonar.core.persistence.SemaphoresImpl;
import org.sonar.core.resource.DefaultResourcePermissions;
import org.sonar.core.rule.CacheRuleFinder;
import org.sonar.core.test.TestPlanBuilder;
import org.sonar.core.test.TestableBuilder;
import org.sonar.core.user.DefaultUserFinder;
import org.sonar.jpa.dao.MeasuresDao;
import org.sonar.jpa.session.DefaultDatabaseConnector;
import org.sonar.jpa.session.JpaDatabaseSession;
import org.sonar.batch.tasks.Tasks;

/**
* Level-3 components. Task-level components that don't depends on project.
*/
public class TaskContainer extends Container {

private static final Logger LOG = LoggerFactory.getLogger(TaskContainer.class);
import javax.annotation.Nullable;

private TaskDefinition taskDefinition;
private boolean projectPresent;
public class TaskContainer extends ComponentContainer {

public TaskContainer(TaskDefinition task, boolean projectPresent) {
this.taskDefinition = task;
this.projectPresent = projectPresent;
public TaskContainer(ComponentContainer parent) {
super(parent);
}

@Override
protected void configure() {
logSettings();
registerCoreComponents();
registerDatabaseComponents();
registerCoreTasks();
if (projectPresent) {
registerCoreComponentsRequiringProject();
}
registerTaskExtensions();
protected void doBeforeStart() {
installTaskExtensions();
installComponentsUsingTaskExtensions();
}

private void registerCoreComponents() {
container.addSingleton(EmailSettings.class);
container.addSingleton(I18nManager.class);
container.addSingleton(RuleI18nManager.class);
container.addSingleton(MeasuresDao.class);
container.addSingleton(CacheRuleFinder.class);
container.addSingleton(CacheMetricFinder.class);
container.addSingleton(DefaultUserFinder.class);
container.addSingleton(ResourceTypes.class);
container.addSingleton(SemaphoreUpdater.class);
container.addSingleton(SemaphoresImpl.class);
container.addSingleton(PastSnapshotFinderByDate.class);
container.addSingleton(PastSnapshotFinderByDays.class);
container.addSingleton(PastSnapshotFinderByPreviousAnalysis.class);
container.addSingleton(PastSnapshotFinderByVersion.class);
container.addSingleton(PastSnapshotFinderByPreviousVersion.class);
container.addSingleton(PastMeasuresLoader.class);
container.addSingleton(PastSnapshotFinder.class);
}
public void executeTask(@Nullable String key) {
startComponents();
TaskDefinition taskDef = get(Tasks.class).getTaskDefinition(key);
if (taskDef != null) {
Task task = get(taskDef.getTask());
if (task != null) {
task.execute();
} else {
throw new IllegalStateException("Command " + key + " is badly defined.");
}

private void registerDatabaseComponents() {
container.addSingleton(JdbcDriverHolder.class);
container.addSingleton(BatchDatabase.class);
container.addSingleton(MyBatis.class);
container.addSingleton(DatabaseVersion.class);
container.addSingleton(DatabaseCompatibility.class);
for (Class daoClass : DaoUtils.getDaoClasses()) {
container.addSingleton(daoClass);
} else {
throw new SonarException("Command " + key + " does not exist.");
}

// hibernate
container.addSingleton(DefaultDatabaseConnector.class);
container.addSingleton(JpaDatabaseSession.class);
container.addSingleton(BatchDatabaseSessionFactory.class);
}

private void registerCoreTasks() {
container.addSingleton(ListTasksTask.class);
if (projectPresent) {
container.addSingleton(ScanTask.class);
}
}

private void registerTaskExtensions() {
ExtensionInstaller installer = container.getComponentByType(ExtensionInstaller.class);
installer.installTaskExtensions(container, projectPresent);
private void installTaskExtensions() {
get(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() {
public boolean accept(Object extension) {
return ExtensionUtils.isType(extension, TaskExtension.class);
}
});
}

private void registerCoreComponentsRequiringProject() {
container.addSingleton(DefaultResourceCreationLock.class);
container.addSingleton(DefaultPersistenceManager.class);
container.addSingleton(DependencyPersister.class);
container.addSingleton(EventPersister.class);
container.addSingleton(LinkPersister.class);
container.addSingleton(MeasurePersister.class);
container.addSingleton(MemoryOptimizer.class);
container.addSingleton(DefaultResourcePermissions.class);
container.addSingleton(DefaultResourcePersister.class);
container.addSingleton(SourcePersister.class);
container.addSingleton(DefaultNotificationManager.class);
container.addSingleton(MetricProvider.class);
container.addSingleton(ProjectExclusions.class);
container.addSingleton(ProjectReactorReady.class);
container.addSingleton(ProjectTree.class);
container.addSingleton(ProjectConfigurator.class);
container.addSingleton(DefaultIndex.class);
container.addSingleton(DefaultFileLinesContextFactory.class);
container.addSingleton(ProjectLock.class);
container.addSingleton(DryRunDatabase.class);

// graphs
container.addSingleton(ScanGraph.create());
container.addSingleton(TestPlanBuilder.class);
container.addSingleton(TestableBuilder.class);
container.addSingleton(ScanPerspectives.class);
container.addSingleton(ScanGraphStore.class);
private void installComponentsUsingTaskExtensions() {
add(ResourceTypes.class, MetricProvider.class, Tasks.class);
}

private void logSettings() {
LOG.info("------------- Executing {}", taskDefinition.getName());
}

/**
* Execute task
*/
@Override
protected void doStart() {
Task task = container.getComponentByType(taskDefinition.getTask());
if (task != null) {
task.execute();
} else {
throw new SonarException("Extension " + taskDefinition.getTask() + " was not found in declared extensions.");
}
}

}

+ 25
- 14
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java View File

@@ -24,8 +24,10 @@ 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.GlobalBatchProperties;
import org.sonar.batch.bootstrap.Container;
import org.sonar.batch.bootstrap.BootstrapProperties;
import org.sonar.batch.bootstrap.TaskContainer;
import org.sonar.batch.scan.ScanTask;
import org.sonar.batch.tasks.ListTasksTask;
import org.sonar.core.PicoUtils;

import java.util.Collections;
@@ -48,11 +50,12 @@ public final class Batch {
private Batch(Builder builder) {
components = Lists.newArrayList();
components.addAll(builder.components);
components.add(builder.environment);
if (builder.environment != null) {
components.add(builder.environment);
}
if (builder.globalProperties != null) {
globalProperties.putAll(builder.globalProperties);
}
else {
} else {
// For backward compatibility, previously all properties were set in root project
globalProperties.putAll(Maps.fromProperties(builder.projectReactor.getRoot().getProperties()));
}
@@ -80,18 +83,29 @@ public final class Batch {
}

private void startBatch() {
Container bootstrapModule = null;
BootstrapContainer bootstrapModule = null;
try {
bootstrapModule = new BootstrapContainer(new GlobalBatchProperties(globalProperties), taskCommand,
projectReactor, components.toArray(new Object[components.size()]));
bootstrapModule.init();
bootstrapModule.start();
List all = Lists.newArrayList(components);
all.add(new BootstrapProperties(globalProperties));
all.add(projectReactor);

bootstrapModule = BootstrapContainer.create(all);
bootstrapModule.startComponents();

TaskContainer taskContainer = new TaskContainer(bootstrapModule);
taskContainer.add(
ScanTask.DEFINITION, ScanTask.class,
ListTasksTask.DEFINITION, ListTasksTask.class
);

taskContainer.executeTask(taskCommand);

} catch (RuntimeException e) {
PicoUtils.propagateStartupException(e);
} finally {
try {
if (bootstrapModule != null) {
bootstrapModule.stop();
bootstrapModule.stopComponents();
}
} catch (Exception e) {
// never throw exceptions in a finally block
@@ -164,9 +178,6 @@ public final class Batch {
}

public Batch build() {
if (environment == null) {
throw new IllegalStateException("EnvironmentInfo is not set");
}
if (components == null) {
throw new IllegalStateException("Batch components are not set");
}

+ 7
- 7
sonar-batch/src/main/java/org/sonar/batch/phases/Phases.java View File

@@ -89,11 +89,11 @@ public final class Phases {
/**
* Executed on each module
*/
public void execute(Project project) {
pi.execute(project);
eventBus.fireEvent(new ProjectAnalysisEvent(project, true));
mavenPluginsConfigurator.execute(project);
mavenPhaseExecutor.execute(project);
public void execute(Project module) {
pi.execute(module);
eventBus.fireEvent(new ProjectAnalysisEvent(module, true));
mavenPluginsConfigurator.execute(module);
mavenPhaseExecutor.execute(module);
initializersExecutor.execute();
fsLogger.log();

@@ -103,7 +103,7 @@ public final class Phases {
persistenceManager.dump();
persistenceManager.setDelayedMode(false);

if (project.isRoot()) {
if (module.isRoot()) {
graphStorage.save();
if (updateStatusJob != null) {
updateStatusJob.execute();
@@ -111,7 +111,7 @@ public final class Phases {
postJobsExecutor.execute(sensorContext);
}
cleanMemory();
eventBus.fireEvent(new ProjectAnalysisEvent(project, false));
eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
}

private void cleanMemory() {

+ 149
- 0
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java View File

@@ -0,0 +1,149 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.scan;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.resources.Languages;
import org.sonar.api.resources.Project;
import org.sonar.api.scan.filesystem.FileExclusions;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.batch.DefaultProfileLoader;
import org.sonar.batch.DefaultProjectClasspath;
import org.sonar.batch.DefaultSensorContext;
import org.sonar.batch.DefaultTimeMachine;
import org.sonar.batch.ProfileProvider;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.ResourceFilters;
import org.sonar.batch.ViolationFilters;
import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ExtensionUtils;
import org.sonar.batch.bootstrap.ProjectSettings;
import org.sonar.batch.bootstrap.UnsupportedProperties;
import org.sonar.batch.components.TimeMachineConfiguration;
import org.sonar.batch.events.EventBus;
import org.sonar.batch.index.DefaultIndex;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.batch.local.DryRunExporter;
import org.sonar.batch.phases.Phases;
import org.sonar.batch.phases.PhasesTimeProfiler;
import org.sonar.batch.scan.filesystem.DeprecatedFileSystemAdapter;
import org.sonar.batch.scan.filesystem.ExclusionFilters;
import org.sonar.batch.scan.filesystem.FileSystemLogger;
import org.sonar.batch.scan.filesystem.LanguageFilters;
import org.sonar.batch.scan.filesystem.ModuleFileSystemProvider;

public class ModuleScanContainer extends ComponentContainer {
private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class);
private final Project module;

public ModuleScanContainer(ProjectScanContainer parent, Project module) {
super(parent);
this.module = module;
}

@Override
protected void doBeforeStart() {
LOG.info("------------- Scan {}", module.getName());
addCoreComponents();
addExtensions();
}

private void addCoreComponents() {
ProjectDefinition moduleDefinition = get(ProjectTree.class).getProjectDefinition(module);
add(
moduleDefinition,
module.getConfiguration(),
module,
ProjectSettings.class);

// hack to initialize commons-configuration before ExtensionProviders
get(ProjectSettings.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());
}

private void addExtensions() {
ExtensionInstaller installer = get(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)) {
// Special use-case: the extension point ProjectBuilder is used in a Maven environment to define some
// new sub-projects without pom.
// Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom.
return !ExtensionUtils.isMavenExtensionOnly(extension) || module.getPom() != null;
}
return false;
}
});
}

@Override
protected void doAfterStart() {
DefaultIndex index = get(DefaultIndex.class);
index.setCurrentProject(module,
get(ResourceFilters.class),
get(ViolationFilters.class),
get(RulesProfile.class));

get(Phases.class).execute(module);
}


}

+ 136
- 0
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java View File

@@ -0,0 +1,136 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.scan;

import org.sonar.api.BatchExtension;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project;
import org.sonar.batch.DefaultFileLinesContextFactory;
import org.sonar.batch.DefaultResourceCreationLock;
import org.sonar.batch.ProjectConfigurator;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ExtensionUtils;
import org.sonar.batch.bootstrap.MetricProvider;
import org.sonar.batch.bootstrap.ProjectExclusions;
import org.sonar.batch.bootstrap.ProjectLock;
import org.sonar.batch.bootstrap.ProjectReactorReady;
import org.sonar.batch.index.DefaultIndex;
import org.sonar.batch.index.DefaultPersistenceManager;
import org.sonar.batch.index.DefaultResourcePersister;
import org.sonar.batch.index.DependencyPersister;
import org.sonar.batch.index.EventPersister;
import org.sonar.batch.index.LinkPersister;
import org.sonar.batch.index.MeasurePersister;
import org.sonar.batch.index.MemoryOptimizer;
import org.sonar.batch.index.SourcePersister;
import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
import org.sonar.batch.scan.maven.MavenPluginExecutor;
import org.sonar.core.component.ScanGraph;
import org.sonar.core.component.ScanGraphStore;
import org.sonar.core.component.ScanPerspectives;
import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.core.resource.DefaultResourcePermissions;
import org.sonar.core.test.TestPlanBuilder;
import org.sonar.core.test.TestableBuilder;

public class ProjectScanContainer extends ComponentContainer {
public ProjectScanContainer(ComponentContainer taskContainer) {
super(taskContainer);
}

@Override
protected void doBeforeStart() {
addBatchComponents();
fixMavenExecutor();
addBatchExtensions();
}

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
);
}

private void fixMavenExecutor() {
if (get(MavenPluginExecutor.class) == null) {
add(FakeMavenPluginExecutor.class);
}
}

private void addBatchExtensions() {
get(ExtensionInstaller.class).install(this, new ExtensionInstaller.ComponentFilter() {
public boolean accept(Object extension) {
return ExtensionUtils.isType(extension, BatchExtension.class)
&& ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH);
}
});
}

@Override
protected void doAfterStart() {
ProjectTree tree = get(ProjectTree.class);
scanRecursively(tree.getRootProject());
}

private void scanRecursively(Project module) {
for (Project subModules : module.getModules()) {
scanRecursively(subModules);
}
scan(module);
}

private void scan(Project module) {
ModuleScanContainer moduleContainer = new ModuleScanContainer(this, module);
try {
moduleContainer.startComponents();
} finally {
moduleContainer.stopComponents();
removeChild();
}
}


}

+ 0
- 149
sonar-batch/src/main/java/org/sonar/batch/scan/ScanContainer.java View File

@@ -1,149 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.scan;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.resources.Languages;
import org.sonar.api.resources.Project;
import org.sonar.api.scan.filesystem.FileExclusions;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.batch.DefaultProfileLoader;
import org.sonar.batch.DefaultProjectClasspath;
import org.sonar.batch.DefaultSensorContext;
import org.sonar.batch.DefaultTimeMachine;
import org.sonar.batch.ProfileProvider;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.ResourceFilters;
import org.sonar.batch.ViolationFilters;
import org.sonar.batch.bootstrap.Container;
import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ProjectSettings;
import org.sonar.batch.bootstrap.UnsupportedProperties;
import org.sonar.batch.components.TimeMachineConfiguration;
import org.sonar.batch.events.EventBus;
import org.sonar.batch.index.DefaultIndex;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.batch.local.DryRunExporter;
import org.sonar.batch.phases.Phases;
import org.sonar.batch.phases.PhasesTimeProfiler;
import org.sonar.batch.scan.filesystem.DeprecatedFileSystemAdapter;
import org.sonar.batch.scan.filesystem.ExclusionFilters;
import org.sonar.batch.scan.filesystem.FileSystemLogger;
import org.sonar.batch.scan.filesystem.LanguageFilters;
import org.sonar.batch.scan.filesystem.ModuleFileSystemProvider;
import org.sonar.core.qualitymodel.DefaultModelFinder;
import org.sonar.jpa.dao.ProfilesDao;
import org.sonar.jpa.dao.RulesDao;

public class ScanContainer extends Container {
private static final Logger LOG = LoggerFactory.getLogger(ScanContainer.class);
private Project project;

public ScanContainer(Project project) {
this.project = project;
}

@Override
protected void configure() {
logSettings();
addCoreComponents();
addPluginExtensions();
}

private void addCoreComponents() {
ProjectDefinition projectDefinition = container.getComponentByType(ProjectTree.class).getProjectDefinition(project);
container.addSingleton(projectDefinition);
container.addSingleton(project.getConfiguration());
container.addSingleton(project);
container.addSingleton(ProjectSettings.class);

// hack to initialize commons-configuration before ExtensionProviders
container.getComponentByType(ProjectSettings.class);

container.addSingleton(EventBus.class);
container.addSingleton(Phases.class);
container.addSingleton(PhasesTimeProfiler.class);
for (Class clazz : Phases.getPhaseClasses()) {
container.addSingleton(clazz);
}
container.addSingleton(UnsupportedProperties.class);

for (Object component : projectDefinition.getContainerExtensions()) {
container.addSingleton(component);
}
container.addSingleton(Languages.class);
container.addSingleton(RulesDao.class);
container.addSingleton(LastSnapshots.class);

// file system
container.addSingleton(PathResolver.class);
container.addSingleton(FileExclusions.class);
container.addSingleton(LanguageFilters.class);
container.addSingleton(ExclusionFilters.class);
container.addSingleton(DefaultProjectClasspath.class);
container.addPicoAdapter(new ModuleFileSystemProvider());
container.addSingleton(DeprecatedFileSystemAdapter.class);
container.addSingleton(FileSystemLogger.class);


// the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor)
container.addSingleton(container.getComponentByType(ResourcePersister.class).getSnapshot(project));

container.addSingleton(TimeMachineConfiguration.class);
container.addSingleton(org.sonar.api.database.daos.MeasuresDao.class);
container.addSingleton(ProfilesDao.class);
container.addSingleton(DefaultSensorContext.class);
container.addSingleton(BatchExtensionDictionnary.class);
container.addSingleton(DefaultTimeMachine.class);
container.addSingleton(ViolationFilters.class);
container.addSingleton(ResourceFilters.class);
container.addSingleton(DefaultModelFinder.class);
container.addSingleton(DefaultProfileLoader.class);
container.addSingleton(DryRunExporter.class);
container.addPicoAdapter(new ProfileProvider());
}

private void addPluginExtensions() {
ExtensionInstaller installer = container.getComponentByType(ExtensionInstaller.class);
installer.installInspectionExtensions(container);
}

private void logSettings() {
LOG.info("------------- Inspecting {}", project.getName());
}

/**
* Analyze project
*/
@Override
protected void doStart() {
DefaultIndex index = container.getComponentByType(DefaultIndex.class);
index.setCurrentProject(project,
container.getComponentByType(ResourceFilters.class),
container.getComponentByType(ViolationFilters.class),
container.getComponentByType(RulesProfile.class));

container.getComponentByType(Phases.class).execute(project);
}
}

+ 9
- 29
sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java View File

@@ -19,53 +19,33 @@
*/
package org.sonar.batch.scan;

import com.google.common.annotations.VisibleForTesting;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project;
import org.sonar.api.task.Task;
import org.sonar.api.task.TaskDefinition;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.tasks.RequiresProject;
import org.sonar.batch.bootstrap.TaskContainer;

@RequiresProject
public class ScanTask implements Task {

public static final String COMMAND = "inspect";

public static final TaskDefinition DEFINITION = TaskDefinition.create()
.setDescription("Scan project and upload report to server")
.setName("Project Scan")
.setCommand(COMMAND)
.setTask(ScanTask.class);

private final ComponentContainer container;
private final ProjectTree projectTree;
private final ComponentContainer taskContainer;

public ScanTask(ProjectTree projectTree, ComponentContainer container) {
this.container = container;
this.projectTree = projectTree;
public ScanTask(TaskContainer taskContainer) {
this.taskContainer = taskContainer;
}

public void execute() {
scanRecursively(projectTree.getRootProject());
}

private void scanRecursively(Project project) {
for (Project subProject : project.getModules()) {
scanRecursively(subProject);
}
scan(project);
}

@VisibleForTesting
void scan(Project project) {
ScanContainer projectModule = new ScanContainer(project);
ProjectScanContainer projectScanContainer = new ProjectScanContainer(taskContainer);
try {
ComponentContainer childContainer = container.createChild();
projectModule.init(childContainer);
projectModule.start();
projectScanContainer.startComponents();
} finally {
projectModule.stop();
container.removeChild();
projectScanContainer.stopComponents();
taskContainer.removeChild();
}
}


+ 8
- 12
sonar-batch/src/main/java/org/sonar/batch/tasks/Tasks.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.batch.tasks;

import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +33,6 @@ import org.sonar.batch.scan.ScanTask;

import javax.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

@@ -44,8 +44,8 @@ public class Tasks implements TaskComponent {
private final TaskDefinition[] taskDefinitions;
private final Settings settings;

private final Map<String, TaskDefinition> taskDefByCommand = new HashMap<String, TaskDefinition>();
private final Map<Class<? extends Task>, TaskDefinition> taskDefByTask = new HashMap<Class<? extends Task>, TaskDefinition>();
private final Map<String, TaskDefinition> taskDefByCommand = Maps.newHashMap();
private final Map<Class<? extends Task>, TaskDefinition> taskDefByTask = Maps.newHashMap();

public Tasks(Settings settings, TaskDefinition[] taskDefinitions) {
this.settings = settings;
@@ -53,13 +53,9 @@ public class Tasks implements TaskComponent {
}

public TaskDefinition getTaskDefinition(@Nullable String command) {
String finalCommand = command;
if (StringUtils.isBlank(finalCommand)) {
// Try with a property
finalCommand = settings.getString(CoreProperties.TASK);
}
// Default to inspection task
finalCommand = StringUtils.isNotBlank(finalCommand) ? finalCommand : ScanTask.COMMAND;
String finalCommand = StringUtils.defaultIfBlank(command, settings.getString(CoreProperties.TASK));
finalCommand = StringUtils.defaultIfBlank(finalCommand, ScanTask.COMMAND);

if (taskDefByCommand.containsKey(finalCommand)) {
return taskDefByCommand.get(finalCommand);
}
@@ -71,7 +67,7 @@ public class Tasks implements TaskComponent {
}

/**
* Perform validation of tasks definitions
* Perform validation of task definitions
*/
public void start() {
for (TaskDefinition def : taskDefinitions) {
@@ -117,7 +113,7 @@ public class Tasks implements TaskComponent {
}
if (taskDefByTask.containsKey(taskClass)) {
throw new SonarException("Task '" + def.getTask().getName() + "' is defined twice: first by '" + taskDefByTask.get(taskClass).getName() + "' and then by '" + def.getName()
+ "'");
+ "'");
}
taskDefByTask.put(taskClass, def);
}

+ 0
- 78
sonar-batch/src/test/java/org/sonar/batch/BatchTest.java View File

@@ -1,78 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.batch.bootstrap.Container;

import java.util.Properties;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class BatchTest {

@Test
public void shouldExecute() {
FakeModule module = new FakeModule();
module.init();
new Batch(module).execute();

assertThat(module.started, is(true));
assertThat(module.stopped, is(true));
}

public static class FakeModule extends Container {
private boolean started=false;
private boolean stopped=false;

@Override
protected void doStart() {
started = true;
}

@Override
protected void doStop() {
if (!started) {
throw new IllegalStateException("Not started");
}
stopped = true;
}

@Override
protected void configure() {
}
}

@Test
public void shouldConvertCommonsConfigurationToProperties() {
PropertiesConfiguration commonsConf = new PropertiesConfiguration();
commonsConf.setProperty("foo", "Foo");
commonsConf.setProperty("list", "One,Two");
assertThat(commonsConf.getString("list"), is("One"));
assertThat(commonsConf.getStringArray("list")[0], is("One"));
assertThat(commonsConf.getStringArray("list")[1], is("Two"));

Properties props = Batch.convertToProperties(commonsConf);
assertThat(props.getProperty("foo"), is("Foo"));
assertThat(props.getProperty("list"), is("One,Two"));
}
}

+ 23
- 21
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchSettingsTest.java View File

@@ -27,6 +27,7 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.config.PropertyDefinitions;

import java.util.Collections;
import java.util.Map;

import static org.fest.assertions.Assertions.assertThat;
@@ -35,56 +36,55 @@ import static org.mockito.Mockito.when;

public class BatchSettingsTest {

Configuration deprecatedConf;
ServerClient client;
ProjectDefinition project;
ProjectReactor reactor;
BootstrapSettings bootstrapSettings;
ServerClient client = mock(ServerClient.class);
ProjectDefinition project = ProjectDefinition.create().setKey("struts");
ProjectReactor reactor = new ProjectReactor(project);
Configuration deprecatedConf = new BaseConfiguration();
BootstrapSettings bootstrapSettings= new BootstrapSettings(new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);

@Before
public void before() {
project = ProjectDefinition.create();
project.setKey("struts");
reactor = new ProjectReactor(project);
deprecatedConf = new BaseConfiguration();
client = mock(ServerClient.class);
when(client.request("/batch_bootstrap/properties?project=struts")).thenReturn(
"[{\"k\":\"sonar.cpd.cross\",\"v\":\"true\"}," +
"{\"k\":\"sonar.java.coveragePlugin\",\"v\":\"jacoco\",\"p\":\"struts\"}," +
"{\"k\":\"sonar.java.coveragePlugin\",\"v\":\"cobertura\",\"p\":\"struts-core\"}]"
);
bootstrapSettings = new BootstrapSettings(reactor, new GlobalBatchProperties());
}

@Test
public void should_load_system_props() {
System.setProperty("BatchSettingsTest.testSystemProp", "system");
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);
assertThat(batchSettings.getString("BatchSettingsTest.testSystemProp")).isEqualTo("system");
}

@Test
public void should_load_build_props() {
project.setProperty("build.prop", "build");
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
assertThat(batchSettings.getString("build.prop")).isEqualTo("build");
public void should_load_project_props() {
project.setProperty("project.prop", "project");
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);
assertThat(batchSettings.getString("project.prop")).isEqualTo("project");
}

@Test
public void should_load_global_settings() {
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);
assertThat(batchSettings.getBoolean("sonar.cpd.cross")).isTrue();
}

@Test
public void should_load_project_root_settings() {
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);
assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco");
}

@Test
public void should_keep_module_settings_for_later() {
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);
Map<String, String> moduleSettings = batchSettings.getModuleProperties("struts-core");
assertThat(moduleSettings).hasSize(1);
assertThat(moduleSettings.get("sonar.java.coveragePlugin")).isEqualTo("cobertura");
@@ -94,13 +94,15 @@ public class BatchSettingsTest {
public void system_props_should_override_build_props() {
System.setProperty("BatchSettingsTest.testSystemProp", "system");
project.setProperty("BatchSettingsTest.testSystemProp", "build");
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);
assertThat(batchSettings.getString("BatchSettingsTest.testSystemProp")).isEqualTo("system");
}

@Test
public void should_forward_to_deprecated_commons_configuration() {
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), reactor, client, deprecatedConf, new GlobalBatchProperties());
BatchSettings batchSettings = new BatchSettings(bootstrapSettings, new PropertyDefinitions(), client, deprecatedConf,
new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);

assertThat(deprecatedConf.getString("sonar.cpd.cross")).isEqualTo("true");
assertThat(deprecatedConf.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco");

+ 18
- 46
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapContainerTest.java View File

@@ -19,68 +19,40 @@
*/
package org.sonar.batch.bootstrap;

import com.google.common.collect.Lists;
import org.junit.Test;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.resources.Project;
import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
import org.sonar.batch.scan.maven.MavenPluginExecutor;
import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
import org.sonar.api.BatchExtension;
import org.sonar.api.config.Settings;
import org.sonar.core.config.Logback;

import static org.fest.assertions.Assertions.assertThat;
import java.util.Collections;

import static org.fest.assertions.Assertions.assertThat;

public class BootstrapContainerTest {

private ProjectReactor reactor = new ProjectReactor(ProjectDefinition.create());

@Test
public void should_register_fake_maven_executor_if_not_maven_env() {
BootstrapContainer module = new BootstrapContainer(reactor, null, MyMavenPluginExecutor.class);
module.init();
public void should_add_components() {
BootstrapContainer container = BootstrapContainer.create(Collections.emptyList());
container.doBeforeStart();

assertThat(module.isMavenPluginExecutorRegistered()).isTrue();
assertThat(module.container.getComponentByType(MavenPluginExecutor.class)).isInstanceOf(MyMavenPluginExecutor.class);
}

@Test
public void should_use_plugin_executor_provided_by_maven() {
BootstrapContainer module = new BootstrapContainer(reactor);
module.init();
assertThat(module.isMavenPluginExecutorRegistered()).isFalse();
assertThat(module.container.getComponentByType(MavenPluginExecutor.class)).isInstanceOf(FakeMavenPluginExecutor.class);
assertThat(container.get(Logback.class)).isNotNull();
assertThat(container.get(TempDirectories.class)).isNotNull();
}

@Test
public void should_register_bootstrap_components() {
BootstrapContainer module = new BootstrapContainer(reactor, new FakeComponent());
module.init();
public void should_add_bootstrap_extensions() {
BootstrapContainer container = BootstrapContainer.create(Lists.newArrayList(Foo.class, new Bar()));
container.doBeforeStart();

assertThat(module.container).isNotNull();
assertThat(module.container.getComponentByType(FakeComponent.class)).isNotNull();
assertThat(module.container.getComponentByType(ProjectReactor.class)).isSameAs(reactor);
assertThat(container.get(Foo.class)).isNotNull();
assertThat(container.get(Bar.class)).isNotNull();
}

@Test
public void should_not_fail_if_no_bootstrap_components() {
BootstrapContainer module = new BootstrapContainer(reactor);
module.init();

assertThat(module.container).isNotNull();
assertThat(module.container.getComponentByType(ProjectReactor.class)).isSameAs(reactor);
}

public static class FakeComponent {
public static class Foo implements BatchExtension {

}

public static class MyMavenPluginExecutor implements MavenPluginExecutor {
public void execute(Project project, DefaultModuleFileSystem fs, String goal) {
}
public static class Bar implements BatchExtension {

public MavenPluginHandler execute(Project project, DefaultModuleFileSystem fs, MavenPluginHandler handler) {
return handler;
}
}
}

sonar-batch/src/main/java/org/sonar/batch/tasks/RequiresProject.java → sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapPropertiesTest.java View File

@@ -17,23 +17,27 @@
* 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.tasks;
package org.sonar.batch.bootstrap;

import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;

/**
* The presence of this annotation on a task extension class indicates that the extension
* will be disabled when there is no project available.
*
* @since 3.5
*/
@Beta
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RequiresProject {
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.MapAssert.entry;

public class BootstrapPropertiesTest {
@Test
public void test_copy_of_properties() {
Map<String, String> map = Maps.newHashMap();
map.put("foo", "bar");

BootstrapProperties wrapper = new BootstrapProperties(map);
assertThat(wrapper.properties()).hasSize(1).includes(entry("foo", "bar"));
assertThat(wrapper.properties()).isNotSameAs(map);

map.put("put", "after_copy");
assertThat(wrapper.properties()).hasSize(1);
}
}

+ 31
- 8
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapSettingsTest.java View File

@@ -19,35 +19,58 @@
*/
package org.sonar.batch.bootstrap;

import com.google.common.collect.ImmutableMap;
import org.junit.Test;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;

import java.util.Collections;
import java.util.Map;

import static org.fest.assertions.Assertions.assertThat;

public class BootstrapSettingsTest {

@Test
public void shouldLoadBuildModel() {
// this is the project as defined in the build tool
public void project_settings_should_be_optional() {
Map<String, String> props = ImmutableMap.of("foo", "bar");
BootstrapSettings settings = new BootstrapSettings(new BootstrapProperties(props));

assertThat(settings.property("foo")).isEqualTo("bar");
}

@Test
public void should_load_project_settings() {
// this is the project as defined in the bootstrapper
ProjectDefinition project = ProjectDefinition.create();
project.setProperty("foo", "bar");

ProjectReactor reactor = new ProjectReactor(project);
BootstrapSettings settings = new BootstrapSettings(reactor, new GlobalBatchProperties());
BootstrapSettings settings = new BootstrapSettings(new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);

assertThat(settings.getProperty("foo")).isEqualTo("bar");
assertThat(settings.property("foo")).isEqualTo("bar");
assertThat(settings.properties().size()).isGreaterThan(1);
}

@Test
public void environmentShouldOverrideBuildModel() {
public void environment_should_override_project_settings() {
ProjectDefinition project = ProjectDefinition.create();
project.setProperty("BootstrapSettingsTest.testEnv", "build");
System.setProperty("BootstrapSettingsTest.testEnv", "env");

ProjectReactor reactor = new ProjectReactor(project);
BootstrapSettings settings = new BootstrapSettings(reactor, new GlobalBatchProperties());
BootstrapSettings settings = new BootstrapSettings(new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);

assertThat(settings.property("BootstrapSettingsTest.testEnv")).isEqualTo("env");
}

@Test
public void should_get_default_value_of_missing_property() {
ProjectDefinition project = ProjectDefinition.create();
project.setProperty("foo", "bar");
ProjectReactor reactor = new ProjectReactor(project);
BootstrapSettings settings = new BootstrapSettings(new BootstrapProperties(Collections.<String, String>emptyMap()), reactor);

assertThat(settings.getProperty("BootstrapSettingsTest.testEnv")).isEqualTo("env");
assertThat(settings.property("foo", "default_value")).isEqualTo("bar");
assertThat(settings.property("missing", "default_value")).isEqualTo("default_value");
}
}

+ 10
- 4
sonar-batch/src/test/java/org/sonar/batch/bootstrap/DryRunDatabaseTest.java View File

@@ -19,10 +19,12 @@
*/
package org.sonar.batch.bootstrap;

import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
@@ -32,6 +34,7 @@ import org.sonar.api.utils.HttpDownloader;
import org.sonar.api.utils.SonarException;

import java.io.File;
import java.io.IOException;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.doThrow;
@@ -52,12 +55,15 @@ public class DryRunDatabaseTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Before
public void setUp() {
databaseFile = new File("/tmp/dryrun.h2.db");
public void setUp() throws Exception {
databaseFile = temp.newFile("dryrun.h2.db");
when(tempDirectories.getFile("", "dryrun.h2.db")).thenReturn(databaseFile);
settings.setProperty(CoreProperties.DRY_RUN, true);
dryRunDatabase = new DryRunDatabase(settings, server, tempDirectories, projectReactor, mock(ProjectReactorReady.class));
dryRunDatabase = new DryRunDatabase(settings, server, tempDirectories, projectReactor);
}

@Test
@@ -83,7 +89,7 @@ public class DryRunDatabaseTest {
assertThat(settings.getString(DatabaseProperties.PROP_DRIVER)).isEqualTo("org.h2.Driver");
assertThat(settings.getString(DatabaseProperties.PROP_USER)).isEqualTo("sonar");
assertThat(settings.getString(DatabaseProperties.PROP_PASSWORD)).isEqualTo("sonar");
assertThat(settings.getString(DatabaseProperties.PROP_URL)).isEqualTo("jdbc:h2:/tmp/dryrun");
assertThat(settings.getString(DatabaseProperties.PROP_URL)).isEqualTo("jdbc:h2:" + StringUtils.removeEnd(databaseFile.getAbsolutePath(), ".h2.db"));
}

@Test

+ 48
- 103
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java View File

@@ -20,22 +20,17 @@
package org.sonar.batch.bootstrap;

import com.google.common.collect.Maps;
import org.apache.commons.lang.ClassUtils;
import org.junit.Test;
import org.sonar.api.BatchExtension;
import org.sonar.api.ExtensionProvider;
import org.sonar.api.Plugin;
import org.sonar.api.ServerExtension;
import org.sonar.api.SonarPlugin;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.SupportedEnvironment;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.resources.Project;
import org.sonar.api.task.TaskDefinition;
import org.sonar.api.task.TaskExtension;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.tasks.RequiresProject;

import java.util.Arrays;
import java.util.List;
@@ -47,173 +42,123 @@ import static org.mockito.Mockito.when;

public class ExtensionInstallerTest {

private static final PluginMetadata METADATA = mock(PluginMetadata.class);
PluginMetadata metadata = mock(PluginMetadata.class);

private static Map<PluginMetadata, Plugin> newPlugin(final Object... extensions) {
Map<PluginMetadata, Plugin> newPlugin(final Object... extensions) {
Map<PluginMetadata, Plugin> result = Maps.newHashMap();
result.put(METADATA,
result.put(metadata,
new SonarPlugin() {
public List<?> getExtensions() {
return Arrays.asList(extensions);
}
}
);
);
return result;
}

@Test
public void shouldInstallExtensionsWithBatchInstantiationStrategy() {
public void should_filter_extensions_to_install() {
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(BatchService.class, ProjectService.class, ServerService.class));
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(Foo.class, Bar.class));
ComponentContainer container = new ComponentContainer();
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings());
installer.install(container, new FooMatcher());

installer.installBatchExtensions(container, InstantiationStrategy.PER_BATCH);

assertThat(container.getComponentByType(BatchService.class)).isNotNull();
assertThat(container.getComponentByType(ProjectService.class)).isNull();
assertThat(container.getComponentByType(ServerService.class)).isNull();
assertThat(container.get(Foo.class)).isNotNull();
assertThat(container.get(Bar.class)).isNull();
}

@Test
public void shouldInstallProvidersWithBatchInstantiationStrategy() {
public void should_execute_extension_provider() {
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(BatchServiceProvider.class, ProjectServiceProvider.class));
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(new FooProvider(), new BarProvider()));
ComponentContainer container = new ComponentContainer();
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings());

installer.installBatchExtensions(container, InstantiationStrategy.PER_BATCH);
installer.install(container, new FooMatcher());

assertThat(container.getComponentByType(BatchService.class)).isNotNull();
assertThat(container.getComponentByType(ProjectService.class)).isNull();
assertThat(container.getComponentByType(ServerService.class)).isNull();
assertThat(container.get(Foo.class)).isNotNull();
assertThat(container.get(Bar.class)).isNull();
}

@Test
public void shouldInstallTaskExtensions() {
public void should_provide_list_of_extensions() {
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(SampleProjectTask.class, SampleTask.class, TaskProvider.class));
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(new FooBarProvider()));
ComponentContainer container = new ComponentContainer();
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings());

installer.installTaskExtensions(container, true);
installer.install(container, new TrueMatcher());

assertThat(container.getComponentByType(SampleProjectTask.class)).isNotNull();
assertThat(container.getComponentByType(SampleTask.class)).isNotNull();
assertThat(container.getComponentByType(AnotherTask.class)).isNotNull();
assertThat(container.get(Foo.class)).isNotNull();
assertThat(container.get(Bar.class)).isNotNull();
}

@Test
public void shouldNotInstallProjectTaskExtensionsWhenNoProject() {
public void should_not_install_on_unsupported_environment() {
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(SampleProjectTask.class, SampleTask.class));
ComponentContainer container = new ComponentContainer();
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings());

installer.installTaskExtensions(container, false);

assertThat(container.getComponentByType(SampleProjectTask.class)).isNull();
assertThat(container.getComponentByType(SampleTask.class)).isNotNull();
}
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(Foo.class, MavenExtension.class, AntExtension.class, new BarProvider()));

@Test
public void shouldInstallTaskDefinitions() {
TaskDefinition definition = TaskDefinition.create();
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(definition));
ComponentContainer container = new ComponentContainer();
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings());

installer.installTaskDefinitionExtensions(container);
installer.install(container, new TrueMatcher());

assertThat(container.getComponentsByType(TaskDefinition.class)).containsExactly(definition);
assertThat(container.get(MavenExtension.class)).isNull();
assertThat(container.get(AntExtension.class)).isNotNull();
assertThat(container.get(Foo.class)).isNotNull();
assertThat(container.get(Bar.class)).isNotNull();
}

@Test
public void shouldNotInstallPluginsOnNonSupportedEnvironment() {
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(MavenService.class, BuildToolService.class));

ComponentContainer container = new ComponentContainer();
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("ant", "1.7"), new Settings());

installer.installInspectionExtensions(container);

assertThat(container.getComponentByType(MavenService.class)).isNull();
assertThat(container.getComponentByType(BuildToolService.class)).isNotNull();
private static class FooMatcher implements ExtensionInstaller.ComponentFilter {
public boolean accept(Object extension) {
return extension.equals(Foo.class) || ClassUtils.isAssignable(Foo.class, extension.getClass()) || ClassUtils.isAssignable(FooProvider.class, extension.getClass());
}
}

@Test
public void should_disable_maven_extensions_if_virtual_module_in_maven_project() {
Project project = mock(Project.class);
when(project.getPom()).thenReturn(null);
BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
when(pluginRepository.getPluginsByMetadata()).thenReturn(newPlugin(MavenService.class));
private static class TrueMatcher implements ExtensionInstaller.ComponentFilter {
public boolean accept(Object extension) {
return true;
}
}

ComponentContainer container = new ComponentContainer();
container.addSingleton(project);
ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, new EnvironmentInformation("maven", "2.2.1"), new Settings());

installer.installInspectionExtensions(container);
public static class Foo implements BatchExtension {

assertThat(container.getComponentByType(MavenService.class)).isNull();
}

@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public static class BatchService implements BatchExtension {
public static class Bar implements BatchExtension {

}

public static class ProjectService implements BatchExtension {
@SupportedEnvironment("maven")
public static class MavenExtension implements BatchExtension {

}

public static class ServerService implements ServerExtension {
@SupportedEnvironment("ant")
public static class AntExtension implements BatchExtension {

}

@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public static class BatchServiceProvider extends ExtensionProvider implements BatchExtension {

public static class FooProvider extends ExtensionProvider implements BatchExtension {
@Override
public Object provide() {
return Arrays.<Object> asList(BatchService.class, ServerService.class);
return new Foo();
}
}

public static class ProjectServiceProvider extends ExtensionProvider implements BatchExtension {
public static class BarProvider extends ExtensionProvider implements BatchExtension {
@Override
public Object provide() {
return ProjectService.class;
return new Bar();
}
}

@SupportedEnvironment("maven")
public static class MavenService implements BatchExtension {

}

@SupportedEnvironment({"maven", "ant", "gradle"})
public static class BuildToolService implements BatchExtension {

}

@RequiresProject
public static class SampleProjectTask implements TaskExtension {

}

public static class SampleTask implements TaskExtension {
}

public static class AnotherTask implements TaskExtension {
}

public static class TaskProvider extends ExtensionProvider implements TaskExtension {

public static class FooBarProvider extends ExtensionProvider implements BatchExtension {
@Override
public Object provide() {
return Arrays.<Object> asList(AnotherTask.class);
return Arrays.asList(new Foo(), new Bar());
}
}


+ 5
- 11
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java View File

@@ -23,7 +23,6 @@ import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -53,7 +52,7 @@ import static org.mockito.Mockito.when;
public class ServerClientTest {

MockHttpServer server = null;
BootstrapSettings settings;
BootstrapSettings settings = mock(BootstrapSettings.class);

@Rule
public TemporaryFolder temp = new TemporaryFolder();
@@ -68,15 +67,10 @@ public class ServerClientTest {
}
}

@Before
public void before(){
settings = mock(BootstrapSettings.class);
}

@Test
public void should_remove_url_ending_slash() throws Exception {
BootstrapSettings settings = mock(BootstrapSettings.class);
when(settings.getProperty(eq("sonar.host.url"), anyString())).thenReturn("http://localhost:8080/sonar/");
when(settings.property(eq("sonar.host.url"), anyString())).thenReturn("http://localhost:8080/sonar/");

ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4"));

@@ -128,8 +122,8 @@ public class ServerClientTest {
server.start();
server.setMockResponseStatus(401);

when(settings.getProperty(eq("sonar.login"))).thenReturn("login");
when(settings.getProperty(eq("sonar.password"))).thenReturn("password");
when(settings.property(eq("sonar.login"))).thenReturn("login");
when(settings.property(eq("sonar.password"))).thenReturn("password");

thrown.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password");
newServerClient().request("/foo");
@@ -146,7 +140,7 @@ public class ServerClientTest {
}

private ServerClient newServerClient() {
when(settings.getProperty(eq("sonar.host.url"), anyString())).thenReturn("http://localhost:" + server.getPort());
when(settings.property(eq("sonar.host.url"), anyString())).thenReturn("http://localhost:" + server.getPort());
return new ServerClient(settings, new EnvironmentInformation("Junit", "4"));
}


+ 0
- 56
sonar-batch/src/test/java/org/sonar/batch/bootstrap/TaskBootstrapContainerTest.java View File

@@ -1,56 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.SonarException;

import static org.mockito.Mockito.mock;

public class TaskBootstrapContainerTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void should_throw_when_no_project_and_task_require_project() {
final ExtensionInstaller extensionInstaller = mock(ExtensionInstaller.class);
Container bootstrapModule = new Container() {
@Override
protected void configure() {
// used to install project extensions
container.addSingleton(extensionInstaller);
container.addSingleton(Settings.class);
}
};
bootstrapModule.init();
TaskBootstrapContainer module = new TaskBootstrapContainer("inspect");
bootstrapModule.installChild(module);

thrown.expect(SonarException.class);
thrown.expectMessage("Task 'Project Scan' requires to be run on a project");

module.start();
}

}

+ 0
- 65
sonar-batch/src/test/java/org/sonar/batch/bootstrap/TaskContainerTest.java View File

@@ -1,65 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.platform.ComponentContainer;
import org.sonar.api.task.TaskDefinition;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class TaskContainerTest {
@Test
public void should_register_task_extensions_when_project_present() {
final ExtensionInstaller extensionInstaller = mock(ExtensionInstaller.class);
Container bootstrapModule = new Container() {
@Override
protected void configure() {
// used to install project extensions
container.addSingleton(extensionInstaller);
}
};
bootstrapModule.init();
TaskContainer module = new TaskContainer(TaskDefinition.create(), true);
bootstrapModule.installChild(module);

verify(extensionInstaller).installTaskExtensions(any(ComponentContainer.class), eq(true));
}

@Test
public void should_register_task_extensions_when_no_project() {
final ExtensionInstaller extensionInstaller = mock(ExtensionInstaller.class);
Container bootstrapModule = new Container() {
@Override
protected void configure() {
// used to install project extensions
container.addSingleton(extensionInstaller);
}
};
bootstrapModule.init();
TaskContainer module = new TaskContainer(TaskDefinition.create(), false);
bootstrapModule.installChild(module);

verify(extensionInstaller).installTaskExtensions(any(ComponentContainer.class), eq(false));
}
}

+ 2
- 7
sonar-batch/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.batch.bootstrapper;

import org.junit.Ignore;
import org.junit.Test;
import org.sonar.api.batch.bootstrap.ProjectReactor;

@@ -41,6 +42,7 @@ public class BatchTest {
.build();
}

@Ignore
@Test(expected = IllegalStateException.class)
public void shouldFailIfNoEnvironment() {
Batch.builder()
@@ -49,13 +51,6 @@ public class BatchTest {
.build();
}

public void shouldNotFailIfNoProjectReactor() {
Batch.builder()
.setEnvironment(new EnvironmentInformation("Gradle", "1.0"))
.addComponent("fake")
.build();
}

@Test(expected = IllegalStateException.class)
public void shouldFailIfNullComponents() {
Batch.builder()

+ 0
- 72
sonar-batch/src/test/java/org/sonar/batch/scan/ScanContainerTest.java View File

@@ -1,72 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.scan;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.fest.assertions.Assertions;
import org.junit.Test;
import org.mockito.Matchers;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.bootstrap.BatchSettings;
import org.sonar.batch.bootstrap.Container;
import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ProjectSettings;
import org.sonar.batch.index.ResourcePersister;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class ScanContainerTest {
@Test
public void should_register_project_extensions() {
// components injected in the parent container
final Project project = new Project("foo");
project.setConfiguration(new PropertiesConfiguration());
final ProjectTree projectTree = mock(ProjectTree.class);
when(projectTree.getProjectDefinition(project)).thenReturn(ProjectDefinition.create());
final ResourcePersister resourcePersister = mock(ResourcePersister.class);
when(resourcePersister.getSnapshot(Matchers.<Resource>any())).thenReturn(new Snapshot());

final ExtensionInstaller extensionInstaller = mock(ExtensionInstaller.class);
Container batchModule = new Container() {
@Override
protected void configure() {
container.addSingleton(extensionInstaller);
container.addSingleton(projectTree);
container.addSingleton(resourcePersister);
container.addSingleton(new BatchSettings());
}
};

batchModule.init();
ScanContainer projectModule = new ScanContainer(project);
batchModule.installChild(projectModule);

verify(extensionInstaller).installInspectionExtensions(any(ComponentContainer.class));
Assertions.assertThat(projectModule.container().getComponentByType(ProjectSettings.class)).isNotNull();
}
}

+ 0
- 59
sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java View File

@@ -1,59 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 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.scan;

import org.junit.Test;
import org.mockito.InOrder;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project;
import org.sonar.batch.ProjectTree;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

public class ScanTaskTest {

@Test
public void should_scan_each_module() {

final Project project = new Project("parent");
final ProjectTree projectTree = mock(ProjectTree.class);
Project module1 = new Project("module1");
module1.setParent(project);
Project module2 = new Project("module2");
module2.setParent(project);

when(projectTree.getRootProject()).thenReturn(project);
ScanTask scanTask = new ScanTask(projectTree, mock(ComponentContainer.class));
ScanTask spy = spy(scanTask);
doNothing().when(spy).scan(any(Project.class));
spy.execute();

InOrder inOrder = inOrder(spy);

inOrder.verify(spy).scan(module1);
inOrder.verify(spy).scan(module2);
inOrder.verify(spy).scan(project);
}
}

+ 4
- 2
sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectBuilder.java View File

@@ -19,7 +19,8 @@
*/
package org.sonar.api.batch.bootstrap;

import org.sonar.api.task.TaskExtension;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.InstantiationStrategy;

/**
* This extension point allows to change project structure at runtime. It is executed once during task startup.
@@ -33,7 +34,8 @@ import org.sonar.api.task.TaskExtension;
*
* @since 2.9
*/
public abstract class ProjectBuilder implements TaskExtension {
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public abstract class ProjectBuilder implements BatchExtension {

private ProjectReactor reactor;


+ 2
- 4
sonar-plugin-api/src/main/java/org/sonar/api/measures/Metrics.java View File

@@ -19,16 +19,14 @@
*/
package org.sonar.api.measures;

import org.sonar.api.BatchExtension;
import org.sonar.api.ServerExtension;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.task.TaskExtension;

import java.util.List;

/**
* @since 1.10
*/
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public interface Metrics extends BatchExtension, ServerExtension {
public interface Metrics extends TaskExtension, ServerExtension {
List<Metric> getMetrics();
}

+ 33
- 2
sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.api.platform;

import com.google.common.collect.Iterables;
import org.picocontainer.Characteristics;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.DefaultPicoContainer;
@@ -56,7 +57,7 @@ public class ComponentContainer implements BatchComponent, ServerComponent {
/**
* Create child container
*/
private ComponentContainer(ComponentContainer parent) {
protected ComponentContainer(ComponentContainer parent) {
this.parent = parent;
this.pico = parent.pico.makeChildContainer();
this.parent.child = this;
@@ -69,10 +70,20 @@ public class ComponentContainer implements BatchComponent, ServerComponent {
* a component twice is not authorized.
*/
public final ComponentContainer startComponents() {
doBeforeStart();
pico.start();
doAfterStart();
return this;
}

protected void doBeforeStart() {

}

protected void doAfterStart() {

}

/**
* This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
* a component twice is not authorized.
@@ -82,6 +93,22 @@ public class ComponentContainer implements BatchComponent, ServerComponent {
return this;
}

/**
* @since 3.5
*/
public final ComponentContainer add(Object... objects) {
for (Object object : objects) {
if (object instanceof ComponentAdapter) {
addPicoAdapter((ComponentAdapter) object);
} else if (object instanceof Iterable) {
add(Iterables.toArray((Iterable) object, Object.class));
} else {
addSingleton(object);
}
}
return this;
}

public final ComponentContainer addSingleton(Object component) {
return addComponent(component, true);
}
@@ -103,7 +130,7 @@ public class ComponentContainer implements BatchComponent, ServerComponent {
}

public final void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
propertyDefinitions.addComponent(extension, plugin!=null ? plugin.getName() : "");
propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : "");
}

public final ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
@@ -111,6 +138,10 @@ public class ComponentContainer implements BatchComponent, ServerComponent {
return this;
}

public <T> T get(Class<T> key) {
return pico.getComponent(key);
}

public final <T> T getComponentByType(Class<T> tClass) {
return pico.getComponent(tClass);
}

+ 2
- 2
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypeTree.java View File

@@ -31,6 +31,7 @@ import com.google.common.collect.Lists;
import org.sonar.api.BatchExtension;
import org.sonar.api.ServerExtension;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.task.TaskExtension;

import javax.annotation.concurrent.Immutable;

@@ -43,8 +44,7 @@ import java.util.List;
*/
@Beta
@Immutable
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public final class ResourceTypeTree implements BatchExtension, ServerExtension {
public final class ResourceTypeTree implements TaskExtension, ServerExtension {

private List<ResourceType> types;
private ListMultimap<String, String> relations;

+ 2
- 1
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypes.java View File

@@ -32,6 +32,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
import org.sonar.api.task.TaskComponent;

import javax.annotation.Nullable;

@@ -44,7 +45,7 @@ import java.util.Map;
* @since 2.14
*/
@Beta
public final class ResourceTypes implements BatchComponent, ServerComponent {
public final class ResourceTypes implements TaskComponent, ServerComponent {

public static final Predicate<ResourceType> AVAILABLE_FOR_FILTERS = new Predicate<ResourceType>() {
public boolean apply(@Nullable ResourceType input) {

+ 42
- 1
sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java View File

@@ -20,12 +20,18 @@
package org.sonar.api.platform;

import org.junit.Test;
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;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

public class ComponentContainerTest {

@@ -38,12 +44,14 @@ public class ComponentContainerTest {

@Test
public void testStartAndStop() {
ComponentContainer container = new ComponentContainer();
ComponentContainer container = spy(new ComponentContainer());
container.addSingleton(StartableComponent.class);
container.startComponents();

assertThat(container.getComponentByType(StartableComponent.class).started).isTrue();
assertThat(container.getComponentByType(StartableComponent.class).stopped).isFalse();
verify(container).doBeforeStart();
verify(container).doAfterStart();

container.stopComponents();
assertThat(container.getComponentByType(StartableComponent.class).stopped).isTrue();
@@ -128,6 +136,29 @@ public class ComponentContainerTest {
assertThat(container.getComponentByType(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();
}

@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();
}

@Test
public void test_add_adapter() {
ComponentContainer container = new ComponentContainer();
container.add(new SimpleComponentProvider());
assertThat(container.get(SimpleComponent.class)).isNotNull();
}

public static class StartableComponent {
public boolean started = false, stopped = false;

@@ -144,4 +175,14 @@ public class ComponentContainerTest {
public static class ComponentWithProperty {

}

public static class SimpleComponent {

}

public static class SimpleComponentProvider extends ProviderAdapter {
public SimpleComponent provide() {
return new SimpleComponent();
}
}
}

+ 8
- 4
sonar-server/src/main/webapp/WEB-INF/app/controllers/batch_bootstrap_controller.rb View File

@@ -30,7 +30,7 @@ class BatchBootstrapController < Api::ApiController
send_data String.from_java_bytes(db_content)
end

# GET /batch_bootstrap/properties?project=<key or id>
# GET /batch_bootstrap/properties?[project=<key or id>]
def properties
json_properties=Property.find(:all, :conditions => ['user_id is null and resource_id is null']).map { |property| to_json_property(property) }

@@ -61,9 +61,13 @@ class BatchBootstrapController < Api::ApiController
private

def load_project
project = Project.by_key(params[:project])
return access_denied if project && !has_role?(:user, project)
project
if params[:project].present?
project = Project.by_key(params[:project])
return access_denied if project && !has_role?(:user, project)
project
else
nil
end
end

def to_json_property(property, project_key=nil)

Loading…
Cancel
Save