diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-10-05 00:44:37 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2011-10-07 13:36:25 +0200 |
commit | ef5bf7fdece84e5a52c8d478c8a733ecdf4d57f1 (patch) | |
tree | c64a8a041183379e92fbc81becc77af07c9b38e3 /sonar-core | |
parent | 3174a29201e922db758b51f4b693ca131e7037ec (diff) | |
download | sonarqube-ef5bf7fdece84e5a52c8d478c8a733ecdf4d57f1.tar.gz sonarqube-ef5bf7fdece84e5a52c8d478c8a733ecdf4d57f1.zip |
SONAR-2861 New Configuration API
The component org.apache.commons.Configuration is still available but plugins should use org.sonar.api.config.Settings.
It also implies the following issues :
SONAR-2870 do not rebuild the WAR file when editing sonar.properties
SONAR-2869 allow to use the annotations @Properties/@Property on extensions
Diffstat (limited to 'sonar-core')
9 files changed, 227 insertions, 54 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/config/ConfigurationUtils.java b/sonar-core/src/main/java/org/sonar/core/config/ConfigurationUtils.java new file mode 100644 index 00000000000..bf4eda351a4 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/config/ConfigurationUtils.java @@ -0,0 +1,125 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.config; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.text.StrSubstitutor; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.configuration.Property; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.jpa.session.DatabaseSessionFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +/** + * @since 2.12 + */ +public final class ConfigurationUtils { + + private ConfigurationUtils() { + } + + public static void copyProperties(Properties from, Map<String, String> to) { + for (Map.Entry<Object, Object> entry : from.entrySet()) { + String key = (String) entry.getKey(); + to.put(key, entry.getValue().toString()); + } + } + + public static Properties openProperties(File file) throws IOException { + FileInputStream input = FileUtils.openInputStream(file); + return openInputStream(input); + } + + /** + * Note that the input stream is closed in this method. + */ + public static Properties openInputStream(InputStream input) throws IOException { + try { + Properties p = new Properties(); + p.load(input); + return p; + + } finally { + IOUtils.closeQuietly(input); + } + } + + public static Properties interpolateEnvVariables(Properties properties) { + return interpolateVariables(properties, System.getenv()); + } + + public static Properties interpolateVariables(Properties properties, Map<String, String> variables) { + Properties result = new Properties(); + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + String value = (String) properties.get(key); + String interpolatedValue = StrSubstitutor.replace(value, variables, "${env:", "}"); + result.setProperty(key, interpolatedValue); + } + return result; + } + + public static List<Property> getProjectProperties(DatabaseSessionFactory dbFactory, String moduleKey) { + DatabaseSession session = prepareDbSession(dbFactory); + ResourceModel resource = session.getSingleResult(ResourceModel.class, "key", moduleKey); + if (resource != null) { + return session + .createQuery("from " + Property.class.getSimpleName() + " p where p.resourceId=:resourceId and p.userId is null") + .setParameter("resourceId", resource.getId()) + .getResultList(); + + } + return Collections.emptyList(); + } + + public static List<Property> getGlobalProperties(DatabaseSessionFactory dbFactory) { + DatabaseSession session = prepareDbSession(dbFactory); + return session + .createQuery("from " + Property.class.getSimpleName() + " p where p.resourceId is null and p.userId is null") + .getResultList(); + + } + + private static DatabaseSession prepareDbSession(DatabaseSessionFactory dbFactory) { + DatabaseSession session = dbFactory.getSession(); + // Ugly workaround before the move to myBatis + // Session is not up-to-date when Ruby on Rails inserts new rows in its own transaction. Seems like + // Hibernate keeps a cache... + session.commit(); + return session; + } + + public static void copyToCommonsConfiguration(Map<String,String> input, Configuration commonsConfig) { + // update deprecated configuration + commonsConfig.clear(); + for (Map.Entry<String, String> entry : input.entrySet()) { + String key = entry.getKey(); + commonsConfig.setProperty(key, entry.getValue()); + } + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java b/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java index 866bb056ece..687576e5650 100644 --- a/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java +++ b/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java @@ -17,7 +17,6 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ - package org.sonar.core.plugins; import com.google.common.collect.Lists; diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java index e543fecc28f..0b27ec5c554 100644 --- a/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java +++ b/sonar-core/src/main/java/org/sonar/jpa/session/AbstractDatabaseConnector.java @@ -24,6 +24,7 @@ import org.apache.commons.lang.StringUtils; import org.hibernate.cfg.Environment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.config.Settings; import org.sonar.api.database.DatabaseProperties; import org.sonar.api.utils.Logs; import org.sonar.jpa.dialect.Dialect; @@ -36,6 +37,7 @@ import javax.persistence.Persistence; import java.sql.Connection; import java.sql.SQLException; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -43,16 +45,15 @@ public abstract class AbstractDatabaseConnector implements DatabaseConnector { protected static final Logger LOG_SQL = LoggerFactory.getLogger("org.hibernate.SQL"); protected static final Logger LOG = LoggerFactory.getLogger(AbstractDatabaseConnector.class); - private Configuration configuration = null; + protected Settings configuration = null; private EntityManagerFactory factory = null; private int databaseVersion = SchemaMigration.VERSION_UNKNOWN; private boolean operational = false; private boolean started = false; private boolean startsFailIfSchemaOutdated; - private Integer transactionIsolation = null; private Dialect dialect = null; - protected AbstractDatabaseConnector(Configuration configuration, boolean startsFailIfSchemaOutdated) { + protected AbstractDatabaseConnector(Settings configuration, boolean startsFailIfSchemaOutdated) { this.configuration = configuration; this.startsFailIfSchemaOutdated = startsFailIfSchemaOutdated; } @@ -60,14 +61,6 @@ public abstract class AbstractDatabaseConnector implements DatabaseConnector { protected AbstractDatabaseConnector() { } - public Configuration getConfiguration() { - return configuration; - } - - public void setConfiguration(Configuration configuration) { - this.configuration = configuration; - } - public String getDialectId() { return dialect.getId(); } @@ -86,18 +79,8 @@ public abstract class AbstractDatabaseConnector implements DatabaseConnector { return started; } - /** - * Get the JDBC transaction isolation defined by the configuration - * - * @return JDBC transaction isolation - */ - public final Integer getTransactionIsolation() { - return transactionIsolation; - } - public void start() { if (!started) { - transactionIsolation = configuration.getInteger(DatabaseProperties.PROP_ISOLATION, null /* use driver default setting */); String jdbcConnectionUrl = testConnection(); dialect = DialectRepository.find(configuration.getString("sonar.jdbc.dialect"), jdbcConnectionUrl); LoggerFactory.getLogger("org.sonar.INFO").info("Database dialect class " + dialect.getClass().getName()); @@ -152,21 +135,15 @@ public abstract class AbstractDatabaseConnector implements DatabaseConnector { protected Properties getHibernateProperties() { Properties props = new Properties(); - if (transactionIsolation != null) { - props.put("hibernate.connection.isolation", Integer.toString(transactionIsolation)); - } - props.put("hibernate.hbm2ddl.auto", getConfiguration().getString(DatabaseProperties.PROP_HIBERNATE_HBM2DLL, "validate")); + props.put("hibernate.hbm2ddl.auto", StringUtils.defaultString(configuration.getString(DatabaseProperties.PROP_HIBERNATE_HBM2DLL), "validate")); props.put(Environment.DIALECT, getDialectClass()); - props.put("hibernate.generate_statistics", getConfiguration().getBoolean(DatabaseProperties.PROP_HIBERNATE_GENERATE_STATISTICS, false)); + props.put("hibernate.generate_statistics", configuration.getBoolean(DatabaseProperties.PROP_HIBERNATE_GENERATE_STATISTICS)); props.put("hibernate.show_sql", Boolean.valueOf(LOG_SQL.isDebugEnabled()).toString()); - Configuration subset = getConfiguration().subset("sonar.hibernate"); - for (Iterator keys = subset.getKeys(); keys.hasNext();) { - String key = (String) keys.next(); - if (StringUtils.isNotBlank((String) subset.getProperty(key))) { - props.put("hibernate." + key, subset.getProperty(key)); - } + List<String> hibernateKeys = configuration.getKeysStartingWith("sonar.hibernate."); + for (String hibernateKey : hibernateKeys) { + props.put(StringUtils.removeStart(hibernateKey, "sonar."), configuration.getString(hibernateKey)); } // custom impl setup diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java index 00e91fd8f38..a2d6c619a89 100644 --- a/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java +++ b/sonar-core/src/main/java/org/sonar/jpa/session/DriverDatabaseConnector.java @@ -19,8 +19,8 @@ */ package org.sonar.jpa.session; -import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; +import org.sonar.api.config.Settings; import org.sonar.api.database.DatabaseProperties; import java.sql.Connection; @@ -35,20 +35,20 @@ public class DriverDatabaseConnector extends AbstractDatabaseConnector { private ClassLoader classloader; private boolean driverProxyRegistered = false; - public DriverDatabaseConnector(Configuration configuration) { + public DriverDatabaseConnector(Settings configuration) { super(configuration, true); this.classloader = getClass().getClassLoader(); } - public DriverDatabaseConnector(Configuration configuration, ClassLoader classloader) { + public DriverDatabaseConnector(Settings configuration, ClassLoader classloader) { super(configuration, true); this.classloader = classloader; } public String getDriver() { - String driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER); + String driver = configuration.getString(DatabaseProperties.PROP_DRIVER); if (driver == null) { - driver = getConfiguration().getString(DatabaseProperties.PROP_DRIVER_DEPRECATED); + driver = configuration.getString(DatabaseProperties.PROP_DRIVER_DEPRECATED); } if (driver == null) { driver = DatabaseProperties.PROP_DRIVER_DEFAULT_VALUE; @@ -57,13 +57,13 @@ public class DriverDatabaseConnector extends AbstractDatabaseConnector { } public String getUrl() { - return getConfiguration().getString(DatabaseProperties.PROP_URL, DatabaseProperties.PROP_URL_DEFAULT_VALUE); + return StringUtils.defaultString(configuration.getString(DatabaseProperties.PROP_URL), DatabaseProperties.PROP_URL_DEFAULT_VALUE); } public String getUsername() { - String username = getConfiguration().getString(DatabaseProperties.PROP_USER); + String username = configuration.getString(DatabaseProperties.PROP_USER); if (username == null) { - username = getConfiguration().getString(DatabaseProperties.PROP_USER_DEPRECATED); + username = configuration.getString(DatabaseProperties.PROP_USER_DEPRECATED); } if (username == null) { username = DatabaseProperties.PROP_USER_DEFAULT_VALUE; @@ -72,7 +72,7 @@ public class DriverDatabaseConnector extends AbstractDatabaseConnector { } public String getPassword() { - return getConfiguration().getString(DatabaseProperties.PROP_PASSWORD, DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE); + return StringUtils.defaultString(configuration.getString(DatabaseProperties.PROP_PASSWORD), DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE); } public Connection getConnection() throws SQLException { diff --git a/sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java b/sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java index 5e9cfa2f09f..863e7ebe7fd 100644 --- a/sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java +++ b/sonar-core/src/main/java/org/sonar/jpa/session/MemoryDatabaseConnector.java @@ -19,8 +19,7 @@ */ package org.sonar.jpa.session; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.PropertiesConfiguration; +import org.sonar.api.config.Settings; import org.sonar.api.database.DatabaseProperties; import org.sonar.jpa.entity.SchemaMigration; @@ -37,7 +36,7 @@ public class MemoryDatabaseConnector extends DriverDatabaseConnector { private int version; - public MemoryDatabaseConnector(Configuration config) { + public MemoryDatabaseConnector(Settings config) { super(config); version = SchemaMigration.LAST_VERSION; } @@ -51,8 +50,8 @@ public class MemoryDatabaseConnector extends DriverDatabaseConnector { this.version = version; } - protected static Configuration getInMemoryConfiguration(boolean createSchema) { - PropertiesConfiguration conf = new PropertiesConfiguration(); + protected static Settings getInMemoryConfiguration(boolean createSchema) { + Settings conf = new Settings(); conf.setProperty(DatabaseProperties.PROP_URL, URL); conf.setProperty(DatabaseProperties.PROP_DRIVER, DRIVER); conf.setProperty(DatabaseProperties.PROP_USER, USER); diff --git a/sonar-core/src/test/java/org/sonar/core/config/ConfigurationUtilsTest.java b/sonar-core/src/test/java/org/sonar/core/config/ConfigurationUtilsTest.java new file mode 100644 index 00000000000..5528102bd4e --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/config/ConfigurationUtilsTest.java @@ -0,0 +1,73 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.config; + +import com.google.common.collect.Maps; +import org.junit.Test; + +import java.util.Map; +import java.util.Properties; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class ConfigurationUtilsTest { + @Test + public void shouldInterpolateVariables() { + Properties input = new Properties(); + input.setProperty("hello", "world"); + input.setProperty("url", "${env:SONAR_JDBC_URL}"); + input.setProperty("do_not_change", "${SONAR_JDBC_URL}"); + Map<String, String> variables = Maps.newHashMap(); + variables.put("SONAR_JDBC_URL", "jdbc:derby:mem"); + + Properties output = ConfigurationUtils.interpolateVariables(input, variables); + + assertThat(output.size(), is(3)); + assertThat(output.getProperty("hello"), is("world")); + assertThat(output.getProperty("url"), is("jdbc:derby:mem")); + assertThat(output.getProperty("do_not_change"), is("${SONAR_JDBC_URL}")); + + // input is not changed + assertThat(input.size(), is(3)); + assertThat(input.getProperty("hello"), is("world")); + assertThat(input.getProperty("url"), is("${env:SONAR_JDBC_URL}")); + assertThat(input.getProperty("do_not_change"), is("${SONAR_JDBC_URL}")); + } + + @Test + public void shouldCopyProperties() { + Properties input = new Properties(); + input.setProperty("hello", "world"); + input.setProperty("foo", "bar"); + Map<String,String> output = Maps.newHashMap(); + + ConfigurationUtils.copyProperties(input, output); + + assertThat(output.size(), is(2)); + assertThat(output.get("hello"), is("world")); + assertThat(output.get("foo"), is("bar")); + + // input is not changed + assertThat(input.size(), is(2)); + assertThat(input.getProperty("hello"), is("world")); + assertThat(input.getProperty("foo"), is("bar")); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java b/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java index afc386a9d8e..7d08e633669 100644 --- a/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java +++ b/sonar-core/src/test/java/org/sonar/core/plugin/AbstractPluginRepositoryTest.java @@ -27,7 +27,6 @@ import org.sonar.api.BatchExtension; import org.sonar.api.ExtensionProvider; import org.sonar.api.Plugin; import org.sonar.api.ServerExtension; -import org.sonar.api.utils.IocContainer; import java.util.Arrays; import java.util.Collection; diff --git a/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java b/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java index f584eb4b72b..8ee37d71933 100644 --- a/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java +++ b/sonar-core/src/test/java/org/sonar/jpa/session/AbstractDatabaseConnectorTest.java @@ -24,6 +24,7 @@ import org.apache.commons.configuration.PropertiesConfiguration; import org.hamcrest.Matchers; import org.junit.Test; import org.mockito.Mockito; +import org.sonar.api.config.Settings; import org.sonar.api.database.DatabaseProperties; import org.sonar.jpa.dialect.HsqlDb; import org.sonar.jpa.dialect.Oracle; @@ -49,7 +50,7 @@ public class AbstractDatabaseConnectorTest { @Test public void useConfiguredDialectByDefault() { - Configuration conf = MemoryDatabaseConnector.getInMemoryConfiguration(false); + Settings conf = MemoryDatabaseConnector.getInMemoryConfiguration(false); conf.setProperty(DatabaseProperties.PROP_DIALECT, DatabaseProperties.DIALECT_ORACLE); TestDatabaseConnector connector = new TestDatabaseConnector(conf); @@ -60,7 +61,7 @@ public class AbstractDatabaseConnectorTest { @Test public void getHibernateProperties() { - PropertiesConfiguration conf = new PropertiesConfiguration(); + Settings conf = new Settings(); conf.setProperty("sonar.foo", "foo value"); // all properties prefixed by sonar.hibernate are propagated to hibernate configuration (the prefix "sonar." is removed) @@ -84,7 +85,7 @@ public class AbstractDatabaseConnectorTest { private class TestDatabaseConnector extends AbstractDatabaseConnector { - public TestDatabaseConnector(Configuration configuration) { + public TestDatabaseConnector(Settings configuration) { super(configuration, false); } diff --git a/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java b/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java index dfc0716eb0d..82fd8c4e41c 100644 --- a/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java +++ b/sonar-core/src/test/java/org/sonar/jpa/session/DriverDatabaseConnectorTest.java @@ -23,6 +23,7 @@ import org.apache.commons.configuration.PropertiesConfiguration; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Test; +import org.sonar.api.config.Settings; import org.sonar.api.database.DatabaseProperties; import java.sql.SQLException; @@ -43,11 +44,10 @@ public class DriverDatabaseConnectorTest { @Test(expected = DatabaseException.class) public void failsIfUnvalidConfiguration() throws SQLException { - PropertiesConfiguration conf = new PropertiesConfiguration(); + Settings conf = new Settings(); conf.setProperty(DatabaseProperties.PROP_URL, "jdbc:foo:bar//xxx"); conf.setProperty(DatabaseProperties.PROP_DRIVER, MemoryDatabaseConnector.DRIVER); conf.setProperty(DatabaseProperties.PROP_USER, "sa"); - conf.setProperty(DatabaseProperties.PROP_PASSWORD, null); connector = new DriverDatabaseConnector(conf); try { connector.start(); @@ -81,7 +81,7 @@ public class DriverDatabaseConnectorTest { @Test public void deprecatedParametersAreStillValid() { - PropertiesConfiguration conf = new PropertiesConfiguration(); + Settings conf = new Settings(); conf.setProperty(DatabaseProperties.PROP_DRIVER_DEPRECATED, MemoryDatabaseConnector.DRIVER); conf.setProperty(DatabaseProperties.PROP_USER_DEPRECATED, "freddy"); connector = new DriverDatabaseConnector(conf); |