SONAR-3895 load settings from web service

This commit is contained in:
Simon Brandhof 2012-11-05 14:22:11 +01:00
parent 81b76d8b6c
commit 3df00fb976
31 changed files with 304 additions and 268 deletions

View File

@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.DryRunIncompatible;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Resource;
@ -30,6 +31,7 @@ import org.sonar.api.security.ResourcePermissions;
import java.util.Set;
@DryRunIncompatible
public class ApplyProjectRolesDecorator implements Decorator {
private final ResourcePermissions resourcePermissions;

View File

@ -20,7 +20,11 @@
package org.sonar.plugins.cpd;
import com.google.common.collect.ImmutableList;
import org.sonar.api.*;
import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.SonarPlugin;
import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator;
import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator;
import org.sonar.plugins.cpd.index.IndexFactory;
@ -37,6 +41,15 @@ import java.util.List;
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CPD_SKIP_PROPERTY,
defaultValue = "false",
name = "Skip",
description = "Disable detection of duplications",
// not displayed in UI
project = false, module = false, global = false,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = PropertyType.BOOLEAN)
})
public final class CpdPlugin extends SonarPlugin {

View File

@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
public class CpdSensor implements Sensor {
@ -34,10 +35,12 @@ public class CpdSensor implements Sensor {
private CpdEngine sonarEngine;
private CpdEngine sonarBridgeEngine;
private Settings settings;
public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine) {
public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine, Settings settings) {
this.sonarEngine = sonarEngine;
this.sonarBridgeEngine = sonarBridgeEngine;
this.settings = settings;
}
public boolean shouldExecuteOnProject(Project project) {
@ -65,9 +68,11 @@ public class CpdSensor implements Sensor {
@VisibleForTesting
boolean isSkipped(Project project) {
Configuration conf = project.getConfiguration();
return conf.getBoolean("sonar.cpd." + project.getLanguageKey() + ".skip",
conf.getBoolean(CoreProperties.CPD_SKIP_PROPERTY, false));
String key = "sonar.cpd." + project.getLanguageKey() + ".skip";
if (settings.hasKey(key)) {
return settings.getBoolean(key);
}
return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY);
}
public void analyse(Project project, SensorContext context) {

View File

@ -38,15 +38,6 @@ public class IndexFactory implements BatchExtension {
private final ResourcePersister resourcePersister;
private final DuplicationDao dao;
/**
* For dry run, where is no access to database.
*/
public IndexFactory(Settings settings) {
this.settings = settings;
this.resourcePersister = null;
this.dao = null;
}
public IndexFactory(Settings settings, ResourcePersister resourcePersister, DuplicationDao dao) {
this.settings = settings;
this.resourcePersister = resourcePersister;
@ -54,23 +45,28 @@ public class IndexFactory implements BatchExtension {
}
public SonarDuplicationsIndex create(Project project) {
if (isCrossProject(project)) {
LOG.info("Cross-project analysis enabled");
if (verifyCrossProject(project, LOG)) {
return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao));
} else {
LOG.info("Cross-project analysis disabled");
return new SonarDuplicationsIndex();
}
return new SonarDuplicationsIndex();
}
/**
* @return true, if was enabled by user and database is available
*/
@VisibleForTesting
boolean isCrossProject(Project project) {
return settings.getBoolean(CoreProperties.CPD_CROSS_RPOJECT)
&& resourcePersister != null && dao != null
&& StringUtils.isBlank(project.getBranch());
}
boolean verifyCrossProject(Project project, Logger logger) {
boolean crossProject = false;
if (settings.getBoolean(CoreProperties.CPD_CROSS_RPOJECT)) {
if (settings.getBoolean("sonar.dryRun")) {
logger.info("Cross-project analysis disabled. Not supported on dry runs.");
} else if (StringUtils.isNotBlank(project.getBranch())) {
logger.info("Cross-project analysis disabled. Not supported on project branches.");
} else {
logger.info("Cross-project analysis enabled");
crossProject = true;
}
} else {
logger.info("Cross-project analysis disabled");
}
return crossProject;
}
}

View File

@ -19,14 +19,14 @@
*/
package org.sonar.plugins.cpd;
import static org.hamcrest.number.OrderingComparisons.greaterThan;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import static org.fest.assertions.Assertions.assertThat;
public class CpdPluginTest {
@Test
public void getExtensions() {
assertThat(new CpdPlugin().getExtensions().size(), greaterThan(1));
assertThat(new CpdPlugin().getExtensions()).hasSize(6);
}
}

View File

@ -22,70 +22,55 @@ package org.sonar.plugins.cpd;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Java;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Project;
import org.sonar.plugins.cpd.index.IndexFactory;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
public class CpdSensorTest {
private SonarEngine sonarEngine;
private SonarBridgeEngine sonarBridgeEngine;
private CpdSensor sensor;
SonarEngine sonarEngine;
SonarBridgeEngine sonarBridgeEngine;
CpdSensor sensor;
Settings settings;
@Before
public void setUp() {
IndexFactory indexFactory = new IndexFactory(null);
IndexFactory indexFactory = mock(IndexFactory.class);
sonarEngine = new SonarEngine(indexFactory);
sonarBridgeEngine = new SonarBridgeEngine(indexFactory);
sensor = new CpdSensor(sonarEngine, sonarBridgeEngine);
settings = new Settings(new PropertyDefinitions(CpdPlugin.class));
sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings);
}
@Test
public void generalSkip() {
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty("sonar.cpd.skip", "true");
Project project = createJavaProject().setConfiguration(conf);
assertTrue(sensor.isSkipped(project));
public void test_global_skip() {
settings.setProperty("sonar.cpd.skip", true);
assertThat(sensor.isSkipped(createJavaProject())).isTrue();
}
@Test
public void doNotSkipByDefault() {
Project project = createJavaProject().setConfiguration(new PropertiesConfiguration());
assertFalse(sensor.isSkipped(project));
public void should_not_skip_by_default() {
assertThat(sensor.isSkipped(createJavaProject())).isFalse();
}
@Test
public void shouldSkipByLanguage() {
Project phpProject = createPhpProject();
phpProject.getConfiguration().setProperty("sonar.cpd.skip", "false");
phpProject.getConfiguration().setProperty("sonar.cpd.php.skip", "true");
assertTrue(sensor.isSkipped(phpProject));
Project javaProject = createJavaProject();
javaProject.getConfiguration().setProperty("sonar.cpd.skip", "false");
javaProject.getConfiguration().setProperty("sonar.cpd.php.skip", "true");
assertFalse(sensor.isSkipped(javaProject));
public void should_skip_by_language() {
settings.setProperty("sonar.cpd.skip", false);
settings.setProperty("sonar.cpd.php.skip", true);
assertThat(sensor.isSkipped(createPhpProject())).isTrue();
assertThat(sensor.isSkipped(createJavaProject())).isFalse();
}
@Test
public void engine() {
Project phpProject = createPhpProject();
Project javaProject = createJavaProject();
assertThat(sensor.getEngine(javaProject), is((CpdEngine) sonarEngine));
assertThat(sensor.getEngine(phpProject), is((CpdEngine) sonarBridgeEngine));
public void test_engine() {
assertThat(sensor.getEngine(createJavaProject())).isSameAs(sonarEngine);
assertThat(sensor.getEngine(createPhpProject())).isSameAs(sonarBridgeEngine);
}
private Project createJavaProject() {

View File

@ -21,54 +21,57 @@ package org.sonar.plugins.cpd.index;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.core.duplication.DuplicationDao;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class IndexFactoryTest {
private Project project;
private Settings settings;
Project project;
Settings settings;
IndexFactory factory;
Logger logger;
@Before
public void setUp() {
project = new Project("foo");
settings = new Settings();
factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
logger = mock(Logger.class);
}
@Test
public void crossProjectEnabled() {
settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
IndexFactory factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
assertThat(factory.isCrossProject(project), is(true));
assertThat(factory.verifyCrossProject(project, logger)).isTrue();
}
@Test
public void noCrossProjectWithBranch() {
settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
IndexFactory factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
project.setBranch("branch");
assertThat(factory.isCrossProject(project), is(false));
assertThat(factory.verifyCrossProject(project, logger)).isFalse();
}
@Test
public void noCrossProjectWithoutDatabase() {
public void cross_project_should_be_disabled_on_dry_run() {
settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
IndexFactory factory = new IndexFactory(settings);
assertThat(factory.isCrossProject(project), is(false));
settings.setProperty("sonar.dryRun", "true");
assertThat(factory.verifyCrossProject(project, logger)).isFalse();
verify(logger).info("Cross-project analysis disabled. Not supported on dry runs.");
}
@Test
public void crossProjectDisabled() {
settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "false");
IndexFactory factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
assertThat(factory.isCrossProject(project), is(false));
assertThat(factory.verifyCrossProject(project, logger)).isFalse();
}
}

View File

@ -34,7 +34,6 @@ 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.config.BatchDatabaseSettingsLoader;
import org.sonar.batch.index.DefaultIndex;
import org.sonar.batch.index.DefaultPersistenceManager;
import org.sonar.batch.index.DefaultResourcePersister;
@ -111,7 +110,7 @@ public class BatchModule extends Module {
container.addSingleton(BatchDatabase.class);
container.addSingleton(MyBatis.class);
container.addSingleton(DatabaseVersion.class);
container.addSingleton(DatabaseBatchCompatibility.class);
container.addSingleton(DatabaseCompatibility.class);
for (Class daoClass : DaoUtils.getDaoClasses()) {
container.addSingleton(daoClass);
}
@ -120,7 +119,6 @@ public class BatchModule extends Module {
container.addSingleton(DefaultDatabaseConnector.class);
container.addSingleton(JpaDatabaseSession.class);
container.addSingleton(BatchDatabaseSessionFactory.class);
container.addSingleton(BatchDatabaseSettingsLoader.class);
}
private void registerBatchExtensions() {

View File

@ -27,6 +27,7 @@ import org.sonar.batch.FakeMavenPluginExecutor;
import org.sonar.batch.MavenPluginExecutor;
import org.sonar.batch.ServerMetadata;
import org.sonar.batch.config.BootstrapSettings;
import org.sonar.batch.config.BootstrapSettingsLoader;
import org.sonar.core.config.Logback;
import org.sonar.wsclient.Sonar;
@ -60,6 +61,7 @@ public class BootstrapModule extends Module {
container.addSingleton(HttpDownloader.class);
container.addSingleton(UriReader.class);
container.addSingleton(PluginDownloader.class);
container.addSingleton(BootstrapSettingsLoader.class);
for (Object component : boostrapperComponents) {
if (component != null) {
container.addSingleton(component);

View File

@ -20,6 +20,7 @@
package org.sonar.batch.bootstrap;
import org.sonar.api.BatchComponent;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.database.DatabaseProperties;
import org.sonar.core.persistence.BadDatabaseVersion;
@ -28,25 +29,27 @@ import org.sonar.core.persistence.DatabaseVersion;
/**
* Detects if database is not up-to-date with the version required by the batch.
*/
public class DatabaseBatchCompatibility implements BatchComponent {
public class DatabaseCompatibility implements BatchComponent {
private DatabaseVersion version;
private Settings settings;
private ServerClient server;
public DatabaseBatchCompatibility(DatabaseVersion version, ServerClient server, Settings settings) {
public DatabaseCompatibility(DatabaseVersion version, ServerClient server, Settings settings) {
this.version = version;
this.server = server;
this.settings = settings;
}
public void start() {
checkCorrectServerId();
checkDatabaseStatus();
if (!settings.getBoolean("sonar.dryRun")) {
checkCorrectServerId();
checkDatabaseStatus();
}
}
private void checkCorrectServerId() {
if (!version.getSonarCoreId().equals(server.getServerId())) {
if (!settings.getString(CoreProperties.SERVER_ID).equals(server.getServerId())) {
StringBuilder message = new StringBuilder("The current batch process and the configured remote server do not share the same DB configuration.\n");
message.append("\t- Batch side: ");
message.append(settings.getString(DatabaseProperties.PROP_URL));

View File

@ -42,7 +42,9 @@ public class ProjectExclusions implements BatchComponent {
private Settings settings;
private ProjectReactor reactor;
public ProjectExclusions(Settings settings, ProjectReactor reactor, ProjectBuilder[] projectBuilders) {
public ProjectExclusions(Settings settings, ProjectReactor reactor,
// exclusions are applied when the project is completely defined by extensions
ProjectBuilder[] projectBuilders) {
this.settings = settings;
this.reactor = reactor;
}

View File

@ -73,8 +73,8 @@ public class ProjectModule extends Module {
}
ProjectDefinition projectDefinition = container.getComponentByType(ProjectTree.class).getProjectDefinition(project);
container.addSingleton(projectDefinition);
container.addSingleton(project);
container.addSingleton(project.getConfiguration());
container.addSingleton(project);
container.addSingleton(ProjectSettings.class);
container.addSingleton(UnsupportedProperties.class);

View File

@ -28,7 +28,7 @@ import org.sonar.core.config.ConfigurationUtils;
/**
* @since 2.12
*/
public final class BootstrapSettings extends Settings {
public class BootstrapSettings extends Settings {
private Configuration deprecatedConfiguration;
private ProjectReactor reactor;

View File

@ -20,44 +20,50 @@
package org.sonar.batch.config;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.core.properties.PropertyDto;
import org.sonar.wsclient.Sonar;
import org.sonar.wsclient.services.Property;
import org.sonar.wsclient.services.PropertyQuery;
import java.util.List;
/**
* @since 2.12
* Load global settings and project settings. Note that the definition of modules is
* incomplete before the execution of ProjectBuilder extensions, so module settings
* are not loaded yet.
* @since 3.4
*/
public final class BatchDatabaseSettingsLoader {
public final class BootstrapSettingsLoader {
private PropertiesDao propertiesDao;
private BootstrapSettings settings;
private ProjectReactor reactor;
private Sonar wsClient;
public BatchDatabaseSettingsLoader(PropertiesDao propertiesDao, BootstrapSettings settings, ProjectReactor reactor) {
this.propertiesDao = propertiesDao;
public BootstrapSettingsLoader(BootstrapSettings settings, ProjectReactor reactor, Sonar wsClient) {
this.settings = settings;
this.reactor = reactor;
this.wsClient = wsClient;
}
public void start() {
LoggerFactory.getLogger(BootstrapSettingsLoader.class).info("Load project settings");
String branch = settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
String projectKey = reactor.getRoot().getKey();
if (StringUtils.isNotBlank(branch)) {
projectKey = String.format("%s:%s", projectKey, branch);
}
setIfNotDefined(propertiesDao.selectProjectProperties(projectKey));
setIfNotDefined(propertiesDao.selectGlobalProperties());
List<Property> wsProperties = wsClient.findAll(PropertyQuery.createForAll().setResourceKeyOrId(projectKey));
for (Property wsProperty : wsProperties) {
setIfNotDefined(wsProperty);
}
settings.updateDeprecatedCommonsConfiguration();
}
private void setIfNotDefined(List<PropertyDto> dbProperties) {
for (PropertyDto dbProperty : dbProperties) {
if (!settings.hasKey(dbProperty.getKey())) {
settings.setProperty(dbProperty.getKey(), dbProperty.getValue());
}
private void setIfNotDefined(Property wsProperty) {
if (!settings.hasKey(wsProperty.getKey())) {
settings.setProperty(wsProperty.getKey(), wsProperty.getValue());
}
}
}

View File

@ -22,14 +22,16 @@ package org.sonar.batch.config;
import com.google.common.collect.Lists;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.core.config.ConfigurationUtils;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.core.properties.PropertyDto;
import org.sonar.wsclient.Sonar;
import org.sonar.wsclient.services.Property;
import org.sonar.wsclient.services.PropertyQuery;
import javax.annotation.Nullable;
import java.util.List;
@ -39,81 +41,77 @@ import java.util.List;
public class ProjectSettings extends Settings {
private Configuration deprecatedCommonsConf;
private ProjectDefinition projectDefinition;
private PropertiesDao propertiesDao;
public ProjectSettings(PropertyDefinitions definitions, ProjectDefinition projectDefinition, PropertiesDao propertiesDao, Project project) {
super(definitions);
this.deprecatedCommonsConf = project.getConfiguration(); // Configuration is not a parameter to be sure that the project conf is used, not the global one
this.projectDefinition = projectDefinition;
this.propertiesDao = propertiesDao;
load();
public ProjectSettings(BootstrapSettings bootstrapSettings, ProjectDefinition project,
Sonar wsClient, Configuration deprecatedCommonsConf) {
super(bootstrapSettings.getDefinitions());
LoggerFactory.getLogger(ProjectSettings.class).info("Load module settings");
this.deprecatedCommonsConf = deprecatedCommonsConf;
if (project.getParent() == null) {
// root project -> no need to reload settings
copy(bootstrapSettings);
} else {
init(project, bootstrapSettings, wsClient);
}
}
public ProjectSettings load() {
clear();
private void copy(BootstrapSettings bootstrapSettings) {
setProperties(bootstrapSettings);
}
// hack to obtain "sonar.branch" before loading settings from database
loadBuildProperties();
private ProjectSettings init(ProjectDefinition project, BootstrapSettings bootstrapSettings, Sonar wsClient) {
addPersistedProperties(project, bootstrapSettings, wsClient);
addBuildProperties(project);
addEnvironmentVariables();
addSystemProperties();
String branch = getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
clear();
// order is important -> bottom-up. The last one overrides all the others.
loadDatabaseGlobalSettings();
loadDatabaseProjectSettings(projectDefinition, branch);
loadBuildProperties();
addEnvironmentVariables();
addSystemProperties();
updateDeprecatedCommonsConfiguration();
return this;
}
private void loadBuildProperties() {
List<ProjectDefinition> orderedProjects = getOrderedProjects(projectDefinition);
private void addPersistedProperties(ProjectDefinition project, BootstrapSettings bootstrapSettings, Sonar wsClient) {
String branch = bootstrapSettings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
String projectKey = project.getKey();
if (StringUtils.isNotBlank(branch)) {
projectKey = String.format("%s:%s", projectKey, branch);
}
List<Property> wsProperties = wsClient.findAll(PropertyQuery.createForAll().setResourceKeyOrId(projectKey));
for (Property wsProperty : wsProperties) {
setProperty(wsProperty.getKey(), wsProperty.getValue());
}
}
private void addBuildProperties(ProjectDefinition project) {
List<ProjectDefinition> orderedProjects = getTopDownParentProjects(project);
for (ProjectDefinition p : orderedProjects) {
addProperties(p.getProperties());
}
}
private void loadDatabaseProjectSettings(ProjectDefinition projectDef, String branch) {
if (projectDef.getParent() != null) {
loadDatabaseProjectSettings(projectDef.getParent(), branch);
}
String projectKey = projectDef.getKey();
if (StringUtils.isNotBlank(branch)) {
projectKey = String.format("%s:%s", projectKey, branch);
}
List<PropertyDto> props = propertiesDao.selectProjectProperties(projectKey);
for (PropertyDto dbProperty : props) {
setProperty(dbProperty.getKey(), dbProperty.getValue());
}
}
private void loadDatabaseGlobalSettings() {
List<PropertyDto> props = propertiesDao.selectGlobalProperties();
for (PropertyDto dbProperty : props) {
setProperty(dbProperty.getKey(), dbProperty.getValue());
}
}
private void updateDeprecatedCommonsConfiguration() {
ConfigurationUtils.copyToCommonsConfiguration(properties, deprecatedCommonsConf);
}
/**
* From root to module
* From root to given project
*/
static List<ProjectDefinition> getOrderedProjects(ProjectDefinition project) {
static List<ProjectDefinition> getTopDownParentProjects(ProjectDefinition project) {
List<ProjectDefinition> result = Lists.newArrayList();
ProjectDefinition pd = project;
while (pd != null) {
result.add(0, pd);
pd = pd.getParent();
ProjectDefinition p = project;
while (p != null) {
result.add(0, p);
p = p.getParent();
}
return result;
}
@Override
protected void doOnSetProperty(String key, @Nullable String value) {
deprecatedCommonsConf.setProperty(key, value);
}
@Override
protected void doOnRemoveProperty(String key) {
deprecatedCommonsConf.clearProperty(key);
}
@Override
protected void doOnClearProperties() {
deprecatedCommonsConf.clear();
}
}

View File

@ -71,6 +71,7 @@ public class DryRunDatabase implements BatchComponent {
return;
}
LOG.info("Install dry run database");
File databaseFile = tempDirectories.getFile("dry_run", "db.h2.db");
downloadDatabase(reactor.getRoot().getKey(), databaseFile);
@ -79,8 +80,6 @@ public class DryRunDatabase implements BatchComponent {
}
private void downloadDatabase(String projectKey, File toFile) {
LOG.info("Downloading DryRun database for project [{}]", projectKey);
try {
server.download(API_SYNCHRO + "?resource=" + projectKey, toFile);
} catch (SonarException e) {
@ -95,8 +94,6 @@ public class DryRunDatabase implements BatchComponent {
}
private void replaceSettings(String databasePath) {
LOG.info("Overriding database settings");
settings
.setProperty("sonar.jdbc.schema", "")
.setProperty(DatabaseProperties.PROP_DIALECT, DIALECT)

View File

@ -23,31 +23,26 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.database.DatabaseProperties;
import org.sonar.api.utils.SonarException;
import org.sonar.core.persistence.BadDatabaseVersion;
import org.sonar.core.persistence.DatabaseVersion;
import java.io.IOException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class DatabaseBatchCompatibilityTest {
public class DatabaseCompatibilityTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private DatabaseVersion databaseVersion;
private ServerClient server;
private Settings settings;
DatabaseVersion databaseVersion;
ServerClient server;
Settings settings;
@Before
public void init() throws Exception {
databaseVersion = mock(DatabaseVersion.class);
when(databaseVersion.getSonarCoreId()).thenReturn("123456");
public void init() {
server = mock(ServerClient.class);
when(server.getURL()).thenReturn("http://localhost:9000");
when(server.getServerId()).thenReturn("123456");
@ -55,6 +50,10 @@ public class DatabaseBatchCompatibilityTest {
settings = new Settings();
settings.setProperty(DatabaseProperties.PROP_URL, "jdbc:postgresql://localhost/foo");
settings.setProperty(DatabaseProperties.PROP_USER, "bar");
settings.setProperty(CoreProperties.SERVER_ID, "123456");
settings.setProperty("sonar.dryRun", false);
databaseVersion = mock(DatabaseVersion.class);
}
@Test
@ -64,7 +63,7 @@ public class DatabaseBatchCompatibilityTest {
thrown.expect(BadDatabaseVersion.class);
thrown.expectMessage("Database relates to a more recent version of Sonar. Please check your settings (JDBC settings, version of Maven plugin)");
new DatabaseBatchCompatibility(databaseVersion, server, settings).start();
new DatabaseCompatibility(databaseVersion, server, settings).start();
}
@Test
@ -74,33 +73,31 @@ public class DatabaseBatchCompatibilityTest {
thrown.expect(BadDatabaseVersion.class);
thrown.expectMessage("Database must be upgraded.");
new DatabaseBatchCompatibility(databaseVersion, server, settings).start();
new DatabaseCompatibility(databaseVersion, server, settings).start();
}
@Test
public void shouldFailIfNotSameServerId() throws Exception {
DatabaseVersion version = mock(DatabaseVersion.class);
when(version.getSonarCoreId()).thenReturn("1111111");
settings.setProperty(CoreProperties.SERVER_ID, "11111111");
thrown.expect(BadDatabaseVersion.class);
thrown.expectMessage("The current batch process and the configured remote server do not share the same DB configuration.");
thrown.expectMessage("- Batch side: jdbc:postgresql://localhost/foo (bar / *****)");
thrown.expectMessage("- Server side: check the configuration at http://localhost:9000/system");
new DatabaseBatchCompatibility(version, server, settings).start();
new DatabaseCompatibility(databaseVersion, server, settings).start();
}
@Test
public void shouldUseDefaultUserNameWhenFaillingIfNotSameServerIdAndNoUserNameFound() throws Exception {
DatabaseVersion version = mock(DatabaseVersion.class);
when(version.getSonarCoreId()).thenReturn("1111111");
settings.setProperty(CoreProperties.SERVER_ID, "11111111");
settings.removeProperty(DatabaseProperties.PROP_USER);
thrown.expect(BadDatabaseVersion.class);
thrown.expectMessage("- Batch side: jdbc:postgresql://localhost/foo (sonar / *****)");
new DatabaseBatchCompatibility(version, server, settings).start();
new DatabaseCompatibility(databaseVersion, server, settings).start();
}
@Test
@ -109,13 +106,23 @@ public class DatabaseBatchCompatibilityTest {
thrown.expect(IllegalStateException.class);
new DatabaseBatchCompatibility(mock(DatabaseVersion.class), server, settings).start();
new DatabaseCompatibility(mock(DatabaseVersion.class), server, settings).start();
}
@Test
public void shouldDoNothingIfUpToDate() {
when(databaseVersion.getStatus()).thenReturn(DatabaseVersion.Status.UP_TO_DATE);
new DatabaseBatchCompatibility(databaseVersion, server, settings).start();
new DatabaseCompatibility(databaseVersion, server, settings).start();
// no error
}
@Test
public void should_not_verify_compatibility_if_dry_run() {
settings.setProperty(CoreProperties.SERVER_ID, "11111111");
settings.setProperty("sonar.dryRun", true);
new DatabaseCompatibility(databaseVersion, server, settings).start();
// no failure
}
}

View File

@ -22,6 +22,8 @@ package org.sonar.batch.bootstrap;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.database.model.Snapshot;
@ -29,9 +31,11 @@ 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.config.BootstrapSettings;
import org.sonar.batch.config.ProjectSettings;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.wsclient.Sonar;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
@ -58,7 +62,8 @@ public class ProjectModuleTest {
container.addSingleton(extensionInstaller);
container.addSingleton(projectTree);
container.addSingleton(resourcePersister);
container.addSingleton(mock(PropertiesDao.class));
container.addSingleton(mock(Sonar.class));
container.addSingleton(mock(BootstrapSettings.class));
}
};

View File

@ -37,7 +37,7 @@ public class ProjectSettingsTest {
grandParent.addSubProject(parent);
parent.addSubProject(child);
List<ProjectDefinition> hierarchy = ProjectSettings.getOrderedProjects(child);
List<ProjectDefinition> hierarchy = ProjectSettings.getTopDownParentProjects(child);
assertThat(hierarchy.get(0), Is.is(grandParent));
assertThat(hierarchy.get(1), Is.is(parent));
assertThat(hierarchy.get(2), Is.is(child));

View File

@ -66,17 +66,6 @@ public class DatabaseVersion implements BatchComponent, ServerComponent {
}
}
public String getSonarCoreId() {
SqlSession session = mybatis.openSession();
try {
PropertyDto serverIdProperty = session.getMapper(PropertiesMapper.class).selectByKey(new PropertyDto().setKey(CoreProperties.SERVER_ID));
// this property can't be NULL
return serverIdProperty.getValue();
} finally {
MyBatis.closeQuietly(session);
}
}
public Status getStatus() {
return getStatus(getVersion(), LAST_VERSION);
}

View File

@ -41,7 +41,7 @@ public class DbTemplate implements ServerComponent {
private static final Logger LOG = LoggerFactory.getLogger(DbTemplate.class);
public DbTemplate copyTable(DataSource source, DataSource dest, String table, String... whereClauses) {
LOG.info("Copy table " + table);
LOG.debug("Copy table %s", table);
String selectQuery = "select * from " + table;
if (whereClauses.length > 0) {

View File

@ -66,6 +66,7 @@ public class DefaultDatabase implements Database {
initSettings();
initDatasource();
checkConnection();
doAfterStart();
return this;
} catch (Exception e) {
@ -73,6 +74,13 @@ public class DefaultDatabase implements Database {
}
}
/**
* Override to execute post-startup code.
*/
protected void doAfterStart() {
}
@VisibleForTesting
void initSettings() {
initProperties();
@ -93,7 +101,7 @@ public class DefaultDatabase implements Database {
if (dialect == null) {
throw new IllegalStateException("Can not guess the JDBC dialect. Please check the property sonar.jdbc.url.");
}
if (H2.ID.equals(dialect.getId())) {
if (H2.ID.equals(dialect.getId()) && !settings.getBoolean("sonar.dryRun")) {
LoggerFactory.getLogger(DefaultDatabase.class).warn("H2 database should be used for evaluation purpose only");
}
if (!properties.containsKey("sonar.jdbc.driverClassName")) {

View File

@ -63,10 +63,6 @@ public class DryRunDatabaseFactory implements ServerComponent {
}
private void copy(DataSource source, DataSource dest, Integer resourceId) {
String notSecured = "NOT (prop_key LIKE '%.secured')";
String defaultProperty = "((user_id IS NULL) AND (resource_id IS NULL))";
String projectProperty = (null == resourceId) ? "" : " OR (resource_id='" + resourceId + "')";
new DbTemplate()
.copyTable(source, dest, "active_rules")
.copyTable(source, dest, "active_rule_parameters")
@ -74,7 +70,6 @@ public class DryRunDatabaseFactory implements ServerComponent {
.copyTable(source, dest, "characteristic_edges")
.copyTable(source, dest, "characteristic_properties")
.copyTable(source, dest, "metrics")
.copyTable(source, dest, "properties", notSecured, defaultProperty + projectProperty)
.copyTable(source, dest, "quality_models")
.copyTable(source, dest, "rules")
.copyTable(source, dest, "rules_parameters")

View File

@ -19,12 +19,10 @@
*/
package org.sonar.core.persistence;
import org.hamcrest.core.Is;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.fest.assertions.Assertions.assertThat;
public class DatabaseVersionTest extends AbstractDaoTestCase {
@Test
@ -33,7 +31,7 @@ public class DatabaseVersionTest extends AbstractDaoTestCase {
Integer version = new DatabaseVersion(getMyBatis()).getVersion();
assertThat(version, Is.is(123));
assertThat(version).isEqualTo(123);
}
@Test
@ -42,23 +40,14 @@ public class DatabaseVersionTest extends AbstractDaoTestCase {
Integer version = new DatabaseVersion(getMyBatis()).getVersion();
assertThat(version, nullValue());
}
@Test
public void getSonarCoreId() {
setupData("getSonarCoreId");
String sonarCoreId = new DatabaseVersion(getMyBatis()).getSonarCoreId();
assertThat(sonarCoreId, is("123456"));
assertThat(version).isNull();
}
@Test
public void getStatus() {
assertThat(DatabaseVersion.getStatus(null, 150), is(DatabaseVersion.Status.FRESH_INSTALL));
assertThat(DatabaseVersion.getStatus(123, 150), is(DatabaseVersion.Status.REQUIRES_UPGRADE));
assertThat(DatabaseVersion.getStatus(150, 150), is(DatabaseVersion.Status.UP_TO_DATE));
assertThat(DatabaseVersion.getStatus(200, 150), is(DatabaseVersion.Status.REQUIRES_DOWNGRADE));
assertThat(DatabaseVersion.getStatus(null, 150)).isEqualTo(DatabaseVersion.Status.FRESH_INSTALL);
assertThat(DatabaseVersion.getStatus(123, 150)).isEqualTo(DatabaseVersion.Status.REQUIRES_UPGRADE);
assertThat(DatabaseVersion.getStatus(150, 150)).isEqualTo(DatabaseVersion.Status.UP_TO_DATE);
assertThat(DatabaseVersion.getStatus(200, 150)).isEqualTo(DatabaseVersion.Status.REQUIRES_DOWNGRADE);
}
}

View File

@ -66,8 +66,8 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
byte[] database = localDatabaseFactory.createDatabaseForDryRun(1);
dataSource = createDatabase(database);
assertThat(rowCount("PROPERTIES")).isEqualTo(2);
assertThat(rowCount("PROJECTS")).isZero();
assertThat(rowCount("metrics")).isEqualTo(2);
assertThat(rowCount("projects")).isZero();
}
private BasicDataSource createDatabase(byte[] db) throws IOException {

View File

@ -1,3 +0,0 @@
<dataset>
<properties id="1" prop_key="sonar.core.id" resource_id="[null]" text_value="123456" user_id="[null]"/>
</dataset>

View File

@ -1,9 +1,8 @@
<dataset>
<properties id="1" prop_key="resourceProperty" text_value="value1" resource_id="1" user_id="[null]"/>
<properties id="2" prop_key="resourceProperty" text_value="value2" resource_id="2" user_id="[null]"/>
<properties id="3" prop_key="globalProperty" text_value="value3" resource_id="[null]" user_id="[null]"/>
<properties id="4" prop_key="userProperty" text_value="value4" resource_id="[null]" user_id="1"/>
<properties id="5" prop_key="property.secured" text_value="value5" resource_id="[null]" user_id="[null]"/>
<metrics id="1" name="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false" delete_historical_data="[null]" />
<metrics id="2" name="coverage" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name=""
enabled="true" worst_value="0" optimized_best_value="true" best_value="100" direction="1" hidden="false" delete_historical_data="[null]" />
<rules_profiles id="1" name="Sonar way with Findbugs" language="java" parent_name="" version="1"
used_profile="false"/>

View File

@ -296,23 +296,23 @@ public class Settings implements BatchComponent, ServerComponent {
}
public final Settings setProperty(String key, @Nullable Boolean value) {
return setProperty(key, value==null ? null : String.valueOf(value));
return setProperty(key, value == null ? null : String.valueOf(value));
}
public final Settings setProperty(String key, @Nullable Integer value) {
return setProperty(key, value==null ? null : String.valueOf(value));
return setProperty(key, value == null ? null : String.valueOf(value));
}
public final Settings setProperty(String key, @Nullable Long value) {
return setProperty(key, value==null ? null : String.valueOf(value));
return setProperty(key, value == null ? null : String.valueOf(value));
}
public final Settings setProperty(String key, @Nullable Double value) {
return setProperty(key, value==null ? null : String.valueOf(value));
return setProperty(key, value == null ? null : String.valueOf(value));
}
public final Settings setProperty(String key, @Nullable Float value) {
return setProperty(key, value==null ? null : String.valueOf(value));
return setProperty(key, value == null ? null : String.valueOf(value));
}
public final Settings setProperty(String key, @Nullable Date date) {
@ -346,6 +346,13 @@ public class Settings implements BatchComponent, ServerComponent {
return addProperties(props);
}
public final Settings setProperties(Settings s) {
if (s.properties==null) {
return clear();
}
return setProperties(Maps.newHashMap(s.properties));
}
public final Settings setProperty(String key, @Nullable Date date, boolean includeTime) {
return setProperty(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date));
}

View File

@ -17,25 +17,45 @@
# License along with Sonar; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
#
require "json"
class Api::PropertiesController < Api::ApiController
before_filter :admin_required, :only => [:create, :update, :destroy]
# curl http://localhost:9000/api/properties -v
# GET /api/properties/index?[resource=<resource id or key>]
# Does NOT manage default values.
def index
properties = Property.find(:all, :conditions => ['resource_id is null and user_id is null']).select do |property|
viewable?(property.key)
keys=Set.new
properties=[]
# project properties
if params[:resource]
resource=Project.by_key(params[:resource])
if resource
# bottom-up projects
projects=[resource].concat(resource.ancestor_projects)
projects.each do |project|
Property.find(:all, :conditions => ['resource_id=? and user_id is null', project.id]).each do |prop|
properties<<prop if keys.add? prop.key
end
end
end
end
# global properties
Property.find(:all, :conditions => 'resource_id is null and user_id is null').each do |prop|
properties<<prop if keys.add? prop.key
end
# apply security
properties = properties.select{|prop| allowed?(prop.key)}
respond_to do |format|
format.json { render :json => jsonp(to_json(properties)) }
format.xml { render :xml => to_xml(properties) }
end
end
# curl http://localhost:9000/api/properties/<key>[?resource=<resource>] -v
# GET /api/properties/<key>[?resource=<resource>]
def show
key = params[:id]
resource_id_or_key = params[:resource]
@ -55,7 +75,7 @@ class Api::PropertiesController < Api::ApiController
format.text { render :text => message, :status => 200 }
end
end
access_denied unless viewable?(key)
access_denied unless allowed?(key)
respond_to do |format|
format.json { render :json => jsonp(to_json([prop])) }
format.xml { render :xml => to_xml([prop]) }
@ -122,8 +142,8 @@ class Api::PropertiesController < Api::ApiController
end
end
def viewable?(property_key)
!property_key.to_s.index('.secured') || is_admin?
def allowed?(property_key)
!property_key.end_with?('.secured') || is_admin?
end
end

View File

@ -131,7 +131,7 @@ class Project < ActiveRecord::Base
' from project_measures m, snapshots s ' +
' where s.id=m.snapshot_id and ' +
" s.status='%s' and " +
' s.project_id=%s and m.metric_id=%s ', Snapshot::STATUS_PROCESSED, self.id, metric_id]) +
' s.project_id=%s and m.metric_id=%s ', 'P', self.id, metric_id]) +
' and m.rule_id IS NULL and m.rule_priority IS NULL' +
' and m.person_id IS NULL' +
' order by s.created_at'

View File

@ -19,28 +19,38 @@
*/
package org.sonar.wsclient.services;
import org.junit.Test;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class PropertyQueryTest extends QueryTestCase {
@Test
public void all() {
assertThat(PropertyQuery.createForAll().getUrl(), is("/api/properties?"));
assertThat(PropertyQuery.createForAll().getModelClass().getName(), is(Property.class.getName()));
public void test_global_properties() {
PropertyQuery query = PropertyQuery.createForAll();
assertThat(query.getUrl(), is("/api/properties?"));
assertThat(query.getModelClass().getName(), is(Property.class.getName()));
}
@Test
public void byKey() {
assertThat(PropertyQuery.createForKey("myprop").getUrl(), is("/api/properties/myprop?"));
assertThat(PropertyQuery.createForKey("myprop").getModelClass().getName(), is(Property.class.getName()));
public void test_project_properties() {
PropertyQuery query = PropertyQuery.createForAll().setResourceKeyOrId("org.apache:struts");
assertThat(query.getUrl(), is("/api/properties?resource=org.apache%3Astruts&"));
assertThat(query.getModelClass().getName(), is(Property.class.getName()));
}
@Test
public void byKeyAndResource() {
assertThat(PropertyQuery.createForResource("myprop", "my:resource").getUrl(), is("/api/properties/myprop?resource=my%3Aresource&"));
assertThat(PropertyQuery.createForResource("myprop", "my:resource").getModelClass().getName(), is(Property.class.getName()));
public void test_global_property() {
PropertyQuery query = PropertyQuery.createForKey("myprop");
assertThat(query.getUrl(), is("/api/properties/myprop?"));
assertThat(query.getModelClass().getName(), is(Property.class.getName()));
}
@Test
public void test_project_property() {
PropertyQuery query = PropertyQuery.createForResource("myprop", "my:resource");
assertThat(query.getUrl(), is("/api/properties/myprop?resource=my%3Aresource&"));
assertThat(query.getModelClass().getName(), is(Property.class.getName()));
}
}