Browse Source

SONAR-3045 Guess the property sonar.jdbc.driverClassName

tags/2.13
Simon Brandhof 12 years ago
parent
commit
76c29aec36

+ 13
- 7
sonar-application/src/main/assembly/conf/sonar.properties View File

@@ -44,9 +44,9 @@ sonar.jdbc.password: sonar
# Note : it does accept connections from remote hosts, so the
# sonar server and the maven plugin must be executed on the same host.
# Comment the following lines to deactivate the default embedded database.
# Comment the following line to deactivate the default embedded database.
sonar.jdbc.url: jdbc:derby://localhost:1527/sonar;create=true
sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver
#sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver
#sonar.jdbc.validationQuery: values(1)

# directory containing Derby database files. By default it's the /data directory in the sonar installation.
@@ -59,8 +59,10 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver


#----- MySQL 5.x/6.x
# Comment the embedded database and uncomment the following properties to use MySQL. The validation query is optional.
# Comment the embedded database and uncomment the following line to use MySQL
#sonar.jdbc.url: jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8

# Optional properties
#sonar.jdbc.driverClassName: com.mysql.jdbc.Driver
#sonar.jdbc.validationQuery: select 1

@@ -71,9 +73,10 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver
# - It's recommended to use the latest version of the JDBC driver (either ojdbc6.jar for Java 6 or ojdbc5.jar for Java 5).
# Download it in http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html
# - Copy the driver to the directory extensions/jdbc-driver/oracle/
# - Comment the embedded database and uncomment the following properties. The validation query is optional.
#
# - Comment the embedded database and uncomment the following line :
#sonar.jdbc.url: jdbc:oracle:thin:@localhost/XE

# Optional properties
#sonar.jdbc.driverClassName: oracle.jdbc.OracleDriver
#sonar.jdbc.validationQuery: select 1 from dual

@@ -84,8 +87,10 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver


#----- PostgreSQL 8.x, 9.x
# Uncomment the following properties to use PostgreSQL. The validation query is optional.
# Comment the embedded database and uncomment the following property to use PostgreSQL
#sonar.jdbc.url: jdbc:postgresql://localhost/sonar

# Optional properties
#sonar.jdbc.driverClassName: org.postgresql.Driver
#sonar.jdbc.validationQuery: select 1

@@ -97,8 +102,9 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver

#----- Microsoft SQLServer
# The Jtds open source driver is available in extensions/jdbc-driver/mssql. More details on http://jtds.sourceforge.net
# The validation query is optional.
#sonar.jdbc.url: jdbc:jtds:sqlserver://localhost/sonar;SelectMethod=Cursor

# Optional properties
#sonar.jdbc.driverClassName: net.sourceforge.jtds.jdbc.Driver
#sonar.jdbc.validationQuery: select 1


+ 8
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java View File

@@ -53,6 +53,14 @@ public class Derby implements Dialect {
return StringUtils.startsWithIgnoreCase(jdbcConnectionURL, "jdbc:derby:");
}

public String getDefaultDriverClassName() {
return "org.apache.derby.jdbc.ClientDriver";
}

public String getConnectionInitStatement(String schema) {
return null;
}

public static class DerbyWithDecimalDialect extends DerbyDialect {
public DerbyWithDecimalDialect() {
super();

+ 6
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java View File

@@ -52,4 +52,10 @@ public interface Dialect {
*/
boolean matchesJdbcURL(String jdbcConnectionURL);

/**
* @since 2.13
*/
String getDefaultDriverClassName();

String getConnectionInitStatement(String schema);
}

+ 7
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java View File

@@ -70,5 +70,12 @@ public class MsSql implements Dialect {
}
}

public String getDefaultDriverClassName() {
return "net.sourceforge.jtds.jdbc.Driver";
}

public String getConnectionInitStatement(String schema) {
return null;
}
}


+ 7
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java View File

@@ -62,4 +62,11 @@ public class MySql implements Dialect {
}
}

public String getDefaultDriverClassName() {
return "com.mysql.jdbc.Driver";
}

public String getConnectionInitStatement(String schema) {
return null;
}
}

+ 10
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java View File

@@ -66,4 +66,14 @@ public class Oracle implements Dialect {
}
}

public String getDefaultDriverClassName() {
return "oracle.jdbc.OracleDriver";
}

public String getConnectionInitStatement(String schema) {
if (StringUtils.isNotBlank(schema)) {
return "ALTER SESSION SET CURRENT_SCHEMA = " + schema;
}
return null;
}
}

+ 10
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java View File

@@ -63,4 +63,14 @@ public class PostgreSql implements Dialect {
}
}

public String getDefaultDriverClassName() {
return "org.postgresql.Driver";
}

public String getConnectionInitStatement(String schema) {
if (StringUtils.isNotBlank(schema)) {
return "SET SEARCH_PATH TO " + schema;
}
return null;
}
}

+ 48
- 42
sonar-core/src/main/java/org/sonar/persistence/DefaultDatabase.java View File

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

import com.google.common.collect.Lists;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.apache.commons.lang.StringUtils;
@@ -33,6 +32,7 @@ import org.sonar.jpa.session.CustomHibernateConnectionProvider;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -47,7 +47,7 @@ public class DefaultDatabase implements Database {
private Settings settings;
private BasicDataSource datasource;
private Dialect dialect;
private String schema;
private Properties properties;

public DefaultDatabase(Settings settings) {
this.settings = settings;
@@ -56,11 +56,8 @@ public class DefaultDatabase implements Database {
public final DefaultDatabase start() {
try {
doBeforeStart();

Properties properties = getProperties();
dialect = initDialect(properties);
schema = initSchema(properties, dialect);
datasource = initDatasource(properties, dialect, schema);
initSettings();
initDatasource();
return this;

} catch (Exception e) {
@@ -68,30 +65,56 @@ public class DefaultDatabase implements Database {
}
}

static Dialect initDialect(Properties properties) {
Dialect result = DialectRepository.find(properties.getProperty("sonar.jdbc.dialect"), properties.getProperty("sonar.jdbc.url"));
if (result != null && Derby.ID.equals(result.getId())) {
void initSettings() {
initProperties();
initDialect();
initSchema();
}

private void initProperties() {
properties = new Properties();
completeProperties(settings, properties, "sonar.jdbc.");
completeProperties(settings, properties, "sonar.hibernate.");
completeDefaultProperties(properties);
doCompleteProperties(properties);
}

private void initDialect() {
dialect = DialectRepository.find(properties.getProperty("sonar.jdbc.dialect"), properties.getProperty("sonar.jdbc.url"));
if (dialect == null) {
throw new IllegalStateException("Can not guess the JDBC dialect. Please check the property sonar.jdbc.url.");
}
if (Derby.ID.equals(dialect.getId())) {
LoggerFactory.getLogger(DefaultDatabase.class).warn("Derby database should be used for evaluation purpose only");
}
return result;
if (!properties.containsKey("sonar.jdbc.driverClassName")) {
properties.setProperty("sonar.jdbc.driverClassName", dialect.getDefaultDriverClassName());
}
}

static String initSchema(Properties properties, Dialect dialect) {
String result = null;
private void initSchema() {
String deprecatedSchema = null;
if (PostgreSql.ID.equals(dialect.getId())) {
result = getSchemaPropertyValue(properties, "sonar.jdbc.postgreSearchPath");
// this property has been deprecated in version 2.13
deprecatedSchema = getSchemaPropertyValue(properties, "sonar.jdbc.postgreSearchPath");

} else if (Oracle.ID.equals(dialect.getId())) {
result = getSchemaPropertyValue(properties, "sonar.hibernate.default_schema");
// this property has been deprecated in version 2.13
deprecatedSchema = getSchemaPropertyValue(properties, "sonar.hibernate.default_schema");
}
if (StringUtils.isNotBlank(deprecatedSchema)) {
properties.setProperty("sonar.jdbc.schema", deprecatedSchema);
}
return StringUtils.isNotBlank(result) ? result : null;
}

static BasicDataSource initDatasource(Properties properties, Dialect dialect, String schema) throws Exception {
private void initDatasource() throws Exception {
LOG.info("Create JDBC datasource");
BasicDataSource result = (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties));
result.setConnectionInitSqls(getConnectionInitStatements(dialect, schema));
return result;
datasource = (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties));

String initStatement = dialect.getConnectionInitStatement(getSchema());
if (StringUtils.isNotBlank(initStatement)) {
datasource.setConnectionInitSqls(Arrays.asList(initStatement));
}
}

protected void doBeforeStart() {
@@ -118,7 +141,7 @@ public class DefaultDatabase implements Database {
}

public final String getSchema() {
return schema;
return properties.getProperty("sonar.jdbc.schema");
}

public Properties getHibernateProperties() {
@@ -133,7 +156,8 @@ public class DefaultDatabase implements Database {
props.put("hibernate.hbm2ddl.auto", "validate");
props.put(Environment.CONNECTION_PROVIDER, CustomHibernateConnectionProvider.class.getName());

if (schema!=null) {
String schema = getSchema();
if (StringUtils.isNotBlank(schema)) {
props.put("hibernate.default_schema", schema);
}
return props;
@@ -144,11 +168,6 @@ public class DefaultDatabase implements Database {
}

public final Properties getProperties() {
Properties properties = new Properties();
completeProperties(settings, properties, "sonar.jdbc.");
completeProperties(settings, properties, "sonar.hibernate.");
completeDefaultProperties(properties);
doCompleteProperties(properties);
return properties;
}

@@ -179,19 +198,6 @@ public class DefaultDatabase implements Database {
return result;
}

static List<String> getConnectionInitStatements(Dialect dialect, String schema) {
List<String> result = Lists.newArrayList();
if (StringUtils.isNotBlank(schema)) {
if (PostgreSql.ID.equals(dialect.getId())) {
result.add("SET SEARCH_PATH TO " + schema);

} else if (Oracle.ID.equals(dialect.getId())) {
result.add("ALTER SESSION SET CURRENT_SCHEMA = " + schema);
}
}
return result;
}

static String getSchemaPropertyValue(Properties props, String deprecatedKey) {
String value = props.getProperty("sonar.jdbc.schema");
if (StringUtils.isBlank(value) && deprecatedKey != null) {
@@ -201,7 +207,7 @@ public class DefaultDatabase implements Database {
}

private static void completeDefaultProperties(Properties props) {
completeDefaultProperty(props, DatabaseProperties.PROP_DRIVER, props.getProperty(DatabaseProperties.PROP_DRIVER_DEPRECATED, DatabaseProperties.PROP_DRIVER_DEFAULT_VALUE));
completeDefaultProperty(props, DatabaseProperties.PROP_DRIVER, props.getProperty(DatabaseProperties.PROP_DRIVER_DEPRECATED));
completeDefaultProperty(props, DatabaseProperties.PROP_URL, DatabaseProperties.PROP_URL_DEFAULT_VALUE);
completeDefaultProperty(props, DatabaseProperties.PROP_USER, props.getProperty(DatabaseProperties.PROP_USER_DEPRECATED, DatabaseProperties.PROP_USER_DEFAULT_VALUE));
completeDefaultProperty(props, DatabaseProperties.PROP_PASSWORD, DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE);
@@ -209,7 +215,7 @@ public class DefaultDatabase implements Database {
}

private static void completeDefaultProperty(Properties props, String key, String defaultValue) {
if (props.getProperty(key) == null) {
if (props.getProperty(key) == null && defaultValue != null) {
props.setProperty(key, defaultValue);
}
}

+ 21
- 12
sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java View File

@@ -20,55 +20,64 @@
package org.sonar.jpa.dialect;

import org.junit.Test;
import static org.junit.Assert.*;
import org.sonar.api.database.DatabaseProperties;
import org.sonar.api.utils.SonarException;

import static org.junit.Assert.assertEquals;

public class DialectRepositoryTest {
@Test
public void testFindById() {
Dialect d = DialectRepository.find(DatabaseProperties.DIALECT_MYSQL, null);
assertEquals(MySql.class, d.getClass());
}
@Test
public void testFindByJdbcUrl() {
Dialect d = DialectRepository.find(null, "jdbc:mysql:foo:bar");
assertEquals(MySql.class, d.getClass());
}
@Test
public void testFindClassName() {
Dialect d = DialectRepository.find(TestDialect.class.getName(), null);
assertEquals(TestDialect.class, d.getClass());
}
@Test(expected=SonarException.class)
@Test(expected = SonarException.class)
public void testFindNoMatch() {
DialectRepository.find("foo", "bar");
}
public static class TestDialect implements Dialect {
public boolean matchesJdbcURL(String jdbcConnectionURL) {
return false;
}

public String getDefaultDriverClassName() {
return null;
}

public String getConnectionInitStatement(String schema) {
return null;
}

public String getId() {
return "testDialect";
}
public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return null;
}
public String getActiveRecordDialectCode() {
return "test";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}
return "jdbc";
}
}

}

+ 21
- 1
sonar-core/src/test/java/org/sonar/jpa/dialect/OracleTest.java View File

@@ -19,9 +19,12 @@
*/
package org.sonar.jpa.dialect;

import org.hamcrest.core.Is;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import org.junit.Test;

public class OracleTest {
@Test
@@ -29,4 +32,21 @@ public class OracleTest {
assertThat(new Oracle().matchesJdbcURL("jdbc:oracle:thin:@localhost/XE"), is(true));
assertThat(new Oracle().matchesJdbcURL("jdbc:hsql:foo"), is(false));
}

/**
* Avoid conflicts with other schemas
*/
@Test
public void shouldChangeOracleSchema() {
String initStatement = new Oracle().getConnectionInitStatement("my_schema");

assertThat(initStatement, Is.is("ALTER SESSION SET CURRENT_SCHEMA = my_schema"));
}

@Test
public void shouldNotChangeOracleSchemaByDefault() {
assertNull(new Oracle().getConnectionInitStatement(null));
}


}

+ 18
- 0
sonar-core/src/test/java/org/sonar/jpa/dialect/PostgreSqlTest.java View File

@@ -19,9 +19,11 @@
*/
package org.sonar.jpa.dialect;

import org.hamcrest.core.Is;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;

public class PostgreSqlTest {
@@ -30,4 +32,20 @@ public class PostgreSqlTest {
assertThat(new PostgreSql().matchesJdbcURL("jdbc:postgresql://localhost/sonar"), is(true));
assertThat(new PostgreSql().matchesJdbcURL("jdbc:hsql:foo"), is(false));
}

/**
* Avoid conflicts with other schemas
*/
@Test
public void shouldChangePostgreSearchPath() {
String initStatement = new PostgreSql().getConnectionInitStatement("my_schema");

assertThat(initStatement, Is.is("SET SEARCH_PATH TO my_schema"));
}

@Test
public void shouldNotChangePostgreSearchPathByDefault() {
assertNull(new PostgreSql().getConnectionInitStatement(null));
}

}

+ 48
- 66
sonar-core/src/test/java/org/sonar/persistence/DefaultDatabaseTest.java View File

@@ -23,12 +23,10 @@ import org.apache.commons.dbcp.BasicDataSource;
import org.hamcrest.core.Is;
import org.junit.Test;
import org.sonar.api.config.Settings;
import org.sonar.jpa.dialect.MySql;
import org.sonar.jpa.dialect.Oracle;
import org.sonar.jpa.dialect.PostgreSql;

import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import static org.hamcrest.Matchers.nullValue;
@@ -44,6 +42,8 @@ public class DefaultDatabaseTest {
@Test
public void shouldLoadDefaultValues() {
DefaultDatabase db = new DefaultDatabase(new Settings());
db.initSettings();

Properties props = db.getProperties();
assertThat(props.getProperty("sonar.jdbc.username"), Is.is("sonar"));
assertThat(props.getProperty("sonar.jdbc.password"), Is.is("sonar"));
@@ -58,6 +58,7 @@ public class DefaultDatabaseTest {
settings.setProperty("sonar.jdbc.user", "me");

DefaultDatabase db = new DefaultDatabase(settings);
db.initSettings();
Properties props = db.getProperties();

assertThat(props.getProperty("sonar.jdbc.username"), Is.is("me"));
@@ -88,6 +89,7 @@ public class DefaultDatabaseTest {
properties.setProperty("sonar.jdbc.maxActive", "2");
}
};
db.initSettings();

Properties props = db.getProperties();

@@ -114,103 +116,83 @@ public class DefaultDatabaseTest {
}
}

/**
* Avoid conflicts with other schemas
*/
@Test
public void shouldChangePostgreSearchPath() {
List<String> statements = DefaultDatabase.getConnectionInitStatements(new PostgreSql(), "my_schema");

assertThat(statements.size(), Is.is(1));
assertThat(statements.get(0), Is.is("SET SEARCH_PATH TO my_schema"));
}

@Test
public void shouldNotChangePostgreSearchPathByDefault() {
List<String> statements = DefaultDatabase.getConnectionInitStatements(new PostgreSql(), null);

assertThat(statements.size(), Is.is(0));
}

/**
* Avoid conflicts with other schemas
*/
@Test
public void shouldAlterOracleSession() {
List<String> statements = DefaultDatabase.getConnectionInitStatements(new Oracle(), "my_schema");

assertThat(statements.size(), Is.is(1));
assertThat(statements.get(0), Is.is("ALTER SESSION SET CURRENT_SCHEMA = my_schema"));
}

@Test
public void shouldNotAlterOracleSessionByDefault() {
List<String> statements = DefaultDatabase.getConnectionInitStatements(new Oracle(), null);

assertThat(statements.size(), Is.is(0));
}

@Test
public void shouldInitPostgresqlSchema() {
Properties props = new Properties();
props.setProperty("sonar.jdbc.schema", "my_schema");
public void shouldInitSchema() {
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.schema", "my_schema");

String schema = DefaultDatabase.initSchema(props, new PostgreSql());
DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, Is.is("my_schema"));
assertThat(database.getSchema(), Is.is("my_schema"));
}

@Test
public void shouldInitPostgresqlSchemaWithDeprecatedProperty() {
Properties props = new Properties();
props.setProperty("sonar.jdbc.postgreSearchPath", "my_schema");
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.dialect", PostgreSql.ID);
settings.setProperty("sonar.jdbc.postgreSearchPath", "my_schema");

String schema = DefaultDatabase.initSchema(props, new PostgreSql());
DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, Is.is("my_schema"));
assertThat(database.getSchema(), Is.is("my_schema"));
}

@Test
public void shouldNotInitPostgresqlSchemaByDefault() {
String schema = DefaultDatabase.initSchema(new Properties(), new PostgreSql());
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.dialect", PostgreSql.ID);

DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, nullValue());
assertThat(database.getSchema(), nullValue());
}

@Test
public void shouldInitOracleSchema() {
Properties props = new Properties();
props.setProperty("sonar.jdbc.schema", "my_schema");
public void shouldInitOracleSchemaWithDeprecatedProperty() {
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.dialect", Oracle.ID);
settings.setProperty("sonar.hibernate.default_schema", "my_schema");

String schema = DefaultDatabase.initSchema(props, new Oracle());
DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, Is.is("my_schema"));
assertThat(database.getSchema(), Is.is("my_schema"));
}

@Test
public void shouldInitOracleSchemaWithDeprecatedProperty() {
Properties props = new Properties();
props.setProperty("sonar.hibernate.default_schema", "my_schema");
public void shouldNotInitOracleSchemaByDefault() {
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.dialect", Oracle.ID);

String schema = DefaultDatabase.initSchema(props, new Oracle());
DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, Is.is("my_schema"));
assertThat(database.getSchema(), nullValue());
}

@Test
public void shouldNotInitOracleSchemaByDefault() {
String schema = DefaultDatabase.initSchema(new Properties(), new Oracle());
public void shouldGuessDialectFromUrl() {
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.url", "jdbc:postgresql://localhost/sonar");

DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, nullValue());
assertThat(database.getDialect().getId(), Is.is(PostgreSql.ID));
}

@Test
public void shouldNotSchemaOnOtherDatabases() {
Properties props = new Properties();
props.setProperty("sonar.jdbc.schema", "my_schema");
public void shouldGuessDefaultDriver() {
Settings settings = new Settings();
settings.setProperty("sonar.jdbc.url", "jdbc:postgresql://localhost/sonar");

String schema = DefaultDatabase.initSchema(props, new MySql());
DefaultDatabase database = new DefaultDatabase(settings);
database.initSettings();

assertThat(schema, nullValue());
assertThat(database.getProperties().getProperty("sonar.jdbc.driverClassName"), Is.is("org.postgresql.Driver"));
}
}

Loading…
Cancel
Save