aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-10-06 00:21:11 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-10-06 15:02:48 +0200
commitf9a7660fc36358f43dba067230e4b39736e46f9b (patch)
tree2a629c800724a4026ebe2cdac1f7ab25eff4079f /sonar-db
parent248208188e2da2983dc6c615218a1dc32131ca56 (diff)
downloadsonarqube-f9a7660fc36358f43dba067230e4b39736e46f9b.tar.gz
sonarqube-f9a7660fc36358f43dba067230e4b39736e46f9b.zip
SONAR-6830 ability to change level of SQL logs on the fly
Diffstat (limited to 'sonar-db')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/Database.java2
-rw-r--r--sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java30
-rw-r--r--sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java32
-rw-r--r--sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java38
-rw-r--r--sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java45
-rw-r--r--sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java27
-rw-r--r--sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java18
-rw-r--r--sonar-db/src/test/java/org/sonar/db/H2Database.java9
-rw-r--r--sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java14
9 files changed, 183 insertions, 32 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/Database.java b/sonar-db/src/main/java/org/sonar/db/Database.java
index 6b747edee76..d2602312ddb 100644
--- a/sonar-db/src/main/java/org/sonar/db/Database.java
+++ b/sonar-db/src/main/java/org/sonar/db/Database.java
@@ -36,4 +36,6 @@ public interface Database extends Startable {
* @return the dialect or null if start() has not been executed
*/
Dialect getDialect();
+
+ void enableSqlLogging(boolean enable);
}
diff --git a/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java b/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java
index 7bb864230f2..b6a044bf3cd 100644
--- a/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java
+++ b/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java
@@ -36,8 +36,12 @@ import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.dialect.Dialect;
import org.sonar.db.dialect.DialectUtils;
+import org.sonar.db.profiling.NullConnectionInterceptor;
+import org.sonar.db.profiling.ProfiledConnectionInterceptor;
import org.sonar.db.profiling.ProfiledDataSource;
+import static java.lang.String.format;
+
/**
* @since 2.12
*/
@@ -51,7 +55,7 @@ public class DefaultDatabase implements Database {
private static final String SONAR_JDBC_URL = "sonar.jdbc.url";
private Settings settings;
- private BasicDataSource datasource;
+ private ProfiledDataSource datasource;
private Dialect dialect;
private Properties properties;
@@ -63,7 +67,7 @@ public class DefaultDatabase implements Database {
public void start() {
try {
initSettings();
- initDatasource();
+ initDataSource();
checkConnection();
} catch (Exception e) {
@@ -82,15 +86,14 @@ public class DefaultDatabase implements Database {
properties.setProperty(DatabaseProperties.PROP_DRIVER, dialect.getDefaultDriverClassName());
}
- private void initDatasource() throws Exception {// NOSONAR this exception is thrown by BasicDataSourceFactory
+ private void initDataSource() throws Exception {
// but it's correctly caught by start()
- LOG.info("Create JDBC datasource for " + properties.getProperty(DatabaseProperties.PROP_URL, DEFAULT_URL));
- datasource = (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties));
+ LOG.info("Create JDBC data source for {}", properties.getProperty(DatabaseProperties.PROP_URL, DEFAULT_URL));
+ BasicDataSource basicDataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties));
+ datasource = new ProfiledDataSource(basicDataSource, NullConnectionInterceptor.INSTANCE);
datasource.setConnectionInitSqls(dialect.getConnectionInitStatements());
datasource.setValidationQuery(dialect.getValidationQuery());
- if ("TRACE".equals(settings.getString("sonar.log.level"))) {
- datasource = new ProfiledDataSource(datasource);
- }
+ enableSqlLogging(datasource, "TRACE".equals(settings.getString("sonar.log.level")));
}
private void checkConnection() {
@@ -129,6 +132,15 @@ public class DefaultDatabase implements Database {
return properties;
}
+ @Override
+ public void enableSqlLogging(boolean enable) {
+ enableSqlLogging(datasource, enable);
+ }
+
+ private static void enableSqlLogging(ProfiledDataSource ds, boolean enable) {
+ ds.setConnectionInterceptor(enable ? ProfiledConnectionInterceptor.INSTANCE : NullConnectionInterceptor.INSTANCE);
+ }
+
/**
* Override this method to add JDBC properties at runtime
*/
@@ -168,6 +180,6 @@ public class DefaultDatabase implements Database {
@Override
public String toString() {
- return "Database[" + (properties != null ? properties.getProperty(SONAR_JDBC_URL) : "?") + "]";
+ return format("Database[%s]", properties != null ? properties.getProperty(SONAR_JDBC_URL) : "?");
}
}
diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java b/sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java
new file mode 100644
index 00000000000..559e953957d
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java
@@ -0,0 +1,32 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.profiling;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.apache.commons.dbcp.BasicDataSource;
+
+public interface ConnectionInterceptor {
+
+ Connection getConnection(BasicDataSource dataSource) throws SQLException;
+
+ Connection getConnection(BasicDataSource dataSource, String login, String password) throws SQLException;
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java b/sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java
new file mode 100644
index 00000000000..0f9949983c3
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.profiling;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.apache.commons.dbcp.BasicDataSource;
+
+public enum NullConnectionInterceptor implements ConnectionInterceptor {
+ INSTANCE;
+
+ @Override
+ public Connection getConnection(BasicDataSource dataSource) throws SQLException {
+ return dataSource.getConnection();
+ }
+
+ @Override
+ public Connection getConnection(BasicDataSource dataSource, String user, String password) throws SQLException {
+ return dataSource.getConnection(user, password);
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java
new file mode 100644
index 00000000000..78220ebfa5a
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.profiling;
+
+import java.lang.reflect.Proxy;
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.apache.commons.dbcp.BasicDataSource;
+
+public enum ProfiledConnectionInterceptor implements ConnectionInterceptor {
+ INSTANCE;
+
+ @Override
+ public Connection getConnection(BasicDataSource dataSource) throws SQLException {
+ return buildConnectionProxy(new ProfilingConnectionHandler(dataSource.getConnection()));
+ }
+
+ @Override
+ public Connection getConnection(BasicDataSource dataSource, String login, String password) throws SQLException {
+ return buildConnectionProxy(new ProfilingConnectionHandler(dataSource.getConnection(login, password)));
+ }
+
+ private static Connection buildConnectionProxy(ProfilingConnectionHandler connectionHandler) {
+ ClassLoader classloader = ProfiledConnectionInterceptor.class.getClassLoader();
+ return (Connection) Proxy.newProxyInstance(classloader, new Class[] {Connection.class}, connectionHandler);
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java
index 2b2136a1ef8..396e2178a17 100644
--- a/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java
+++ b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java
@@ -20,9 +20,9 @@
package org.sonar.db.profiling;
import java.io.PrintWriter;
-import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
import java.util.Collection;
import org.apache.commons.dbcp.BasicDataSource;
import org.sonar.api.utils.log.Logger;
@@ -33,9 +33,19 @@ public class ProfiledDataSource extends BasicDataSource {
static final Logger SQL_LOGGER = Loggers.get("sql");
private final BasicDataSource delegate;
+ private ConnectionInterceptor connectionInterceptor;
- public ProfiledDataSource(BasicDataSource delegate) {
+ public ProfiledDataSource(BasicDataSource delegate, ConnectionInterceptor connectionInterceptor) {
this.delegate = delegate;
+ this.connectionInterceptor = connectionInterceptor;
+ }
+
+ public BasicDataSource getDelegate() {
+ return delegate;
+ }
+
+ public synchronized void setConnectionInterceptor(ConnectionInterceptor ci) {
+ this.connectionInterceptor = ci;
}
@Override
@@ -310,14 +320,12 @@ public class ProfiledDataSource extends BasicDataSource {
@Override
public Connection getConnection() throws SQLException {
- return (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {Connection.class},
- new ProfilingConnectionHandler(delegate.getConnection()));
+ return connectionInterceptor.getConnection(delegate);
}
@Override
- public Connection getConnection(String user, String pass) throws SQLException {
- return (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {Connection.class},
- new ProfilingConnectionHandler(delegate.getConnection(user, pass)));
+ public Connection getConnection(String login, String password) throws SQLException {
+ return connectionInterceptor.getConnection(this, login, password);
}
@Override
@@ -326,6 +334,11 @@ public class ProfiledDataSource extends BasicDataSource {
}
@Override
+ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ return java.util.logging.Logger.getLogger(getClass().getName());
+ }
+
+ @Override
public PrintWriter getLogWriter() throws SQLException {
return delegate.getLogWriter();
}
diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java b/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java
index 4446a47e7ee..b6e4c3566d6 100644
--- a/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java
+++ b/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java
@@ -40,17 +40,17 @@ class ProfilingConnectionHandler implements InvocationHandler {
if ("prepareStatement".equals(method.getName())) {
PreparedStatement statement = (PreparedStatement) result;
String sql = (String) args[0];
- return Proxy.newProxyInstance(ProfilingConnectionHandler.class.getClassLoader(), new Class[] {PreparedStatement.class},
- new ProfilingPreparedStatementHandler(statement, sql));
-
- } else if ("createStatement".equals(method.getName())) {
+ return buildStatementProxy(PreparedStatement.class, new ProfilingPreparedStatementHandler(statement, sql));
+ }
+ if ("createStatement".equals(method.getName())) {
Statement statement = (Statement) result;
- return Proxy.newProxyInstance(ProfilingConnectionHandler.class.getClassLoader(), new Class[] {Statement.class},
- new ProfilingStatementHandler(statement));
-
- } else {
- return result;
+ return buildStatementProxy(Statement.class, new ProfilingStatementHandler(statement));
}
+ return result;
+ }
+
+ private Object buildStatementProxy(Class<? extends Statement> stmtClass, InvocationHandler handler) {
+ return Proxy.newProxyInstance(ProfilingConnectionHandler.class.getClassLoader(), new Class[] {stmtClass}, handler);
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/H2Database.java b/sonar-db/src/test/java/org/sonar/db/H2Database.java
index 688211e31b2..b2ada1cc440 100644
--- a/sonar-db/src/test/java/org/sonar/db/H2Database.java
+++ b/sonar-db/src/test/java/org/sonar/db/H2Database.java
@@ -27,6 +27,8 @@ import org.apache.commons.dbutils.DbUtils;
import org.sonar.db.dialect.Dialect;
import org.sonar.db.dialect.H2;
+import static java.lang.String.format;
+
/**
* H2 in-memory database, used for unit tests only.
*
@@ -109,7 +111,12 @@ public class H2Database implements Database {
}
@Override
+ public void enableSqlLogging(boolean enable) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String toString() {
- return "H2 Database[" + name + "]";
+ return format("H2 Database[%s]", name);
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java b/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java
index d52e95531f0..d58c20e5d9c 100644
--- a/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java
@@ -31,6 +31,7 @@ import org.apache.commons.dbcp.BasicDataSource;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -42,7 +43,7 @@ public class ProfiledDataSourceTest {
public LogTester logTester = new LogTester();
@Test
- public void log_sql_requests() throws Exception {
+ public void execute_and_log_sql_requests() throws Exception {
BasicDataSource originDataSource = mock(BasicDataSource.class);
Connection connection = mock(Connection.class);
@@ -64,7 +65,7 @@ public class ProfiledDataSourceTest {
when(connection.createStatement()).thenReturn(statement);
when(statement.execute(sql)).thenReturn(true);
- ProfiledDataSource ds = new ProfiledDataSource(originDataSource);
+ ProfiledDataSource ds = new ProfiledDataSource(originDataSource, ProfiledConnectionInterceptor.INSTANCE);
assertThat(ds.getUrl()).isNull();
assertThat(ds.getConnection().getClientInfo()).isNull();
@@ -80,14 +81,15 @@ public class ProfiledDataSourceTest {
assertThat(statementProxy.getConnection()).isNull();
assertThat(statementProxy.execute(sql)).isTrue();
- assertThat(logTester.logs()).hasSize(2);
- assertThat(logTester.logs().get(1)).contains(sql);
+ assertThat(logTester.logs(LoggerLevel.TRACE)).hasSize(2);
+ assertThat(logTester.logs(LoggerLevel.TRACE).get(0)).containsSequence("sql=insert into polop (col1, col2, col3, col4) values (?, ?, ?, ?, ?);");
+ assertThat(logTester.logs(LoggerLevel.TRACE).get(1)).containsSequence("sql=select 'polop' from dual;");
}
@Test
- public void delegate_to_underlying_datasource() throws Exception {
+ public void delegate_to_underlying_data_source() throws Exception {
BasicDataSource delegate = mock(BasicDataSource.class);
- ProfiledDataSource proxy = new ProfiledDataSource(delegate);
+ ProfiledDataSource proxy = new ProfiledDataSource(delegate, ProfiledConnectionInterceptor.INSTANCE);
// painful to call all methods
// so using reflection to check that calls does not fail