소스 검색

SONAR-17200 Move to HikariCP from Apache DBCP

tags/9.7.0.61563
Jacek 2 년 전
부모
커밋
8961d0c0b8
31개의 변경된 파일633개의 추가작업 그리고 589개의 파일을 삭제
  1. 2
    1
      build.gradle
  2. 1
    1
      server/sonar-ce/build.gradle
  3. 4
    9
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeDatabaseMBean.java
  4. 10
    71
      server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeDatabaseMBeanImpl.java
  5. 5
    6
      server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CeDatabaseMBeanImplTest.java
  6. 3
    1
      server/sonar-db-core/build.gradle
  7. 52
    27
      server/sonar-db-core/src/main/java/org/sonar/db/DefaultDatabase.java
  8. 3
    3
      server/sonar-db-core/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java
  9. 3
    3
      server/sonar-db-core/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java
  10. 3
    3
      server/sonar-db-core/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java
  11. 191
    310
      server/sonar-db-core/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java
  12. 40
    19
      server/sonar-db-core/src/test/java/org/sonar/db/DefaultDatabaseTest.java
  13. 1
    10
      server/sonar-db-core/src/test/java/org/sonar/db/profiling/InvocationUtilsTest.java
  14. 41
    5
      server/sonar-db-core/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java
  15. 5
    9
      server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreH2Database.java
  16. 75
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/DatabaseMBean.java
  17. 65
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/DatabaseMBeanTest.java
  18. 2
    2
      server/sonar-db-dao/src/testFixtures/java/org/sonar/db/DbTester.java
  19. 4
    7
      server/sonar-docs/src/pages/instance-administration/monitoring.md
  20. 5
    0
      server/sonar-docs/src/pages/setup/upgrade-notes.md
  21. 2
    5
      server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
  22. 3
    4
      server/sonar-process/src/main/java/org/sonar/process/systeminfo/BaseSectionMBean.java
  23. 79
    0
      server/sonar-process/src/test/java/org/sonar/process/systeminfo/BaseSectionMBeanTest.java
  24. 1
    1
      server/sonar-webserver-core/build.gradle
  25. 6
    57
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/DbConnectionSection.java
  26. 11
    17
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/DbConnectionSectionMBean.java
  27. 1
    0
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java
  28. 1
    1
      server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/BaseSectionMBeanTest.java
  29. 10
    11
      server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/DbConnectionSectionTest.java
  30. 1
    0
      server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/FakeSection.java
  31. 3
    6
      sonar-application/src/main/assembly/conf/sonar.properties

+ 2
- 1
build.gradle 파일 보기

@@ -261,7 +261,7 @@ subprojects {
dependency 'org.awaitility:awaitility:4.2.0'
dependency 'org.apache.commons:commons-csv:1.9.0'
dependency 'org.apache.commons:commons-email:1.5'
dependency 'org.apache.commons:commons-dbcp2:2.9.0'
dependency 'com.zaxxer:HikariCP:5.0.1'
dependency('org.apache.httpcomponents:httpclient:4.5.13'){
exclude 'commons-logging:commons-logging'
}
@@ -304,6 +304,7 @@ subprojects {
dependency('org.mockito:mockito-core:4.7.0') {
exclude 'org.hamcrest:hamcrest-core'
}
dependency('org.mockito:mockito-inline:4.7.0')
dependency 'org.mybatis:mybatis:3.5.10'
dependency 'org.nanohttpd:nanohttpd:2.3.1'
dependencySet(group: 'org.slf4j', version: '1.7.30') {

+ 1
- 1
server/sonar-ce/build.gradle 파일 보기

@@ -13,7 +13,7 @@ dependencies {
compile 'com.hazelcast:hazelcast'
compile 'com.hazelcast:hazelcast-kubernetes'
compile 'commons-io:commons-io'
compile 'org.apache.commons:commons-dbcp2'
compile 'com.zaxxer:HikariCP'
compile 'org.sonarsource.api.plugin:sonar-plugin-api'
compile project(':server:sonar-ce-common')
compile project(':server:sonar-ce-task')

+ 4
- 9
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeDatabaseMBean.java 파일 보기

@@ -21,23 +21,18 @@ package org.sonar.ce.monitoring;

public interface CeDatabaseMBean {

String OBJECT_NAME = "SonarQube:name=ComputeEngineDatabaseConnection";

int getPoolActiveConnections();

int getPoolMaxActiveConnections();
int getPoolMaxConnections();

int getPoolIdleConnections();
int getPoolTotalConnections();

int getPoolMaxIdleConnections();
int getPoolIdleConnections();

int getPoolMinIdleConnections();

int getPoolInitialSize();
long getPoolMaxLifeTimeMillis();

long getPoolMaxWaitMillis();

boolean getPoolRemoveAbandoned();

int getPoolRemoveAbandonedTimeoutSeconds();
}

+ 10
- 71
server/sonar-ce/src/main/java/org/sonar/ce/monitoring/CeDatabaseMBeanImpl.java 파일 보기

@@ -19,95 +19,34 @@
*/
package org.sonar.ce.monitoring;

import org.apache.commons.dbcp2.BasicDataSource;
import org.sonar.api.Startable;
import org.sonar.db.DatabaseMBean;
import org.sonar.db.DbClient;
import org.sonar.process.Jmx;
import org.sonar.process.systeminfo.SystemInfoSection;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

public class CeDatabaseMBeanImpl implements CeDatabaseMBean, Startable, SystemInfoSection {
private final DbClient dbClient;
public class CeDatabaseMBeanImpl extends DatabaseMBean implements CeDatabaseMBean {

public CeDatabaseMBeanImpl(DbClient dbClient) {
this.dbClient = dbClient;
}

@Override
public void start() {
Jmx.register(OBJECT_NAME, this);
}

/**
* Unregister, if needed
*/
@Override
public void stop() {
Jmx.unregister(OBJECT_NAME);
}

@Override
public int getPoolActiveConnections() {
return commonsDbcp().getNumActive();
}

@Override
public int getPoolMaxActiveConnections() {
return commonsDbcp().getMaxTotal();
}

@Override
public int getPoolIdleConnections() {
return commonsDbcp().getNumIdle();
}
private static final String OBJECT_NAME = "ComputeEngineDatabaseConnection";

@Override
public int getPoolMaxIdleConnections() {
return commonsDbcp().getMaxIdle();
}

@Override
public int getPoolMinIdleConnections() {
return commonsDbcp().getMinIdle();
}

@Override
public int getPoolInitialSize() {
return commonsDbcp().getInitialSize();
}

@Override
public long getPoolMaxWaitMillis() {
return commonsDbcp().getMaxWaitMillis();
}

@Override
public boolean getPoolRemoveAbandoned() {
return commonsDbcp().getRemoveAbandonedOnBorrow();
public CeDatabaseMBeanImpl(DbClient dbClient) {
super(dbClient);
}

@Override
public int getPoolRemoveAbandonedTimeoutSeconds() {
return commonsDbcp().getRemoveAbandonedTimeout();
}

private BasicDataSource commonsDbcp() {
return (BasicDataSource) dbClient.getDatabase().getDataSource();
protected String name() {
return OBJECT_NAME;
}

@Override
public ProtobufSystemInfo.Section toProtobuf() {
ProtobufSystemInfo.Section.Builder builder = ProtobufSystemInfo.Section.newBuilder();
builder.setName("Compute Engine Database Connection");
builder.addAttributesBuilder().setKey("Pool Initial Size").setLongValue(getPoolInitialSize()).build();
builder.addAttributesBuilder().setKey("Pool Total Connections").setLongValue(getPoolTotalConnections()).build();
builder.addAttributesBuilder().setKey("Pool Active Connections").setLongValue(getPoolActiveConnections()).build();
builder.addAttributesBuilder().setKey("Pool Idle Connections").setLongValue(getPoolIdleConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Active Connections").setLongValue(getPoolMaxActiveConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Idle Connections").setLongValue(getPoolMaxIdleConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Connections").setLongValue(getPoolMaxConnections()).build();
builder.addAttributesBuilder().setKey("Pool Min Idle Connections").setLongValue(getPoolMinIdleConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Wait (ms)").setLongValue(getPoolMaxWaitMillis()).build();
builder.addAttributesBuilder().setKey("Pool Remove Abandoned").setBooleanValue(getPoolRemoveAbandoned()).build();
builder.addAttributesBuilder().setKey("Pool Remove Abandoned Timeout (sec)").setLongValue(getPoolRemoveAbandonedTimeoutSeconds()).build();
builder.addAttributesBuilder().setKey("Pool Max Lifetime (ms)").setLongValue(getPoolMaxLifeTimeMillis()).build();
return builder.build();
}
}

+ 5
- 6
server/sonar-ce/src/test/java/org/sonar/ce/monitoring/CeDatabaseMBeanImplTest.java 파일 보기

@@ -33,7 +33,6 @@ import org.sonar.process.Jmx;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.ce.monitoring.CeDatabaseMBean.OBJECT_NAME;

public class CeDatabaseMBeanImplTest {

@@ -45,7 +44,7 @@ public class CeDatabaseMBeanImplTest {
@BeforeClass
public static void beforeClass() {
// if any other class starts a container where CeDatabaseMBeanImpl is added, it will have been registered
Jmx.unregister(OBJECT_NAME);
Jmx.unregister("SonarQube:name=ComputeEngineDatabaseConnection");
}

@Test
@@ -63,15 +62,15 @@ public class CeDatabaseMBeanImplTest {
public void export_system_info() {
ProtobufSystemInfo.Section section = underTest.toProtobuf();
assertThat(section.getName()).isEqualTo("Compute Engine Database Connection");
assertThat(section.getAttributesCount()).isEqualTo(9);
assertThat(section.getAttributes(0).getKey()).isEqualTo("Pool Initial Size");
assertThat(section.getAttributes(0).getLongValue()).isGreaterThanOrEqualTo(0);
assertThat(section.getAttributesCount()).isEqualTo(7);
assertThat(section.getAttributes(0).getKey()).isEqualTo("Pool Total Connections");
assertThat(section.getAttributes(0).getLongValue()).isPositive();
}

@CheckForNull
private ObjectInstance getMBean() throws Exception {
try {
return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(OBJECT_NAME));
return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName("SonarQube:name=ComputeEngineDatabaseConnection"));
} catch (InstanceNotFoundException e) {
return null;
}

+ 3
- 1
server/sonar-db-core/build.gradle 파일 보기

@@ -12,7 +12,7 @@ dependencies {
compile 'com.google.guava:guava'
compile 'commons-io:commons-io'
compile 'commons-lang:commons-lang'
compile 'org.apache.commons:commons-dbcp2'
compile 'com.zaxxer:HikariCP'
compile 'org.mybatis:mybatis'
compile 'org.slf4j:slf4j-api'
compile 'org.sonarsource.api.plugin:sonar-plugin-api'
@@ -27,7 +27,9 @@ dependencies {
testCompile 'com.oracle.database.jdbc:ojdbc8'
testCompile 'com.tngtech.java:junit-dataprovider'
testCompile 'org.mockito:mockito-core'
testCompile 'org.mockito:mockito-inline'
testCompile 'org.postgresql:postgresql'

testCompile project(':sonar-testing-harness')

testRuntime 'com.h2database:h2'

+ 52
- 27
server/sonar-db-core/src/main/java/org/sonar/db/DefaultDatabase.java 파일 보기

@@ -21,15 +21,15 @@ package org.sonar.db;

import ch.qos.logback.classic.Level;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.utils.log.Logger;
@@ -43,7 +43,12 @@ import org.sonar.process.logging.LogbackHelper;

import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static org.sonar.process.ProcessProperties.Property.JDBC_DRIVER_PATH;
import static org.sonar.process.ProcessProperties.Property.JDBC_EMBEDDED_PORT;
import static org.sonar.process.ProcessProperties.Property.JDBC_MIN_IDLE;
import static org.sonar.process.ProcessProperties.Property.JDBC_PASSWORD;
import static org.sonar.process.ProcessProperties.Property.JDBC_URL;
import static org.sonar.process.ProcessProperties.Property.JDBC_USERNAME;

/**
* @since 2.12
@@ -57,12 +62,25 @@ public class DefaultDatabase implements Database {
private static final String SONAR_JDBC_DIALECT = "sonar.jdbc.dialect";
private static final String SONAR_JDBC_DRIVER = "sonar.jdbc.driverClassName";
private static final String SONAR_JDBC_MAX_ACTIVE = "sonar.jdbc.maxActive";
private static final String DBCP_JDBC_MAX_ACTIVE = "maxTotal";
private static final String SONAR_JDBC_MAX_WAIT = "sonar.jdbc.maxWait";
private static final String DBCP_JDBC_MAX_WAIT = "maxWaitMillis";
private static final Map<String, String> SONAR_JDBC_TO_DBCP_PROPERTY_MAPPINGS = ImmutableMap.of(
SONAR_JDBC_MAX_ACTIVE, DBCP_JDBC_MAX_ACTIVE,
SONAR_JDBC_MAX_WAIT, DBCP_JDBC_MAX_WAIT);
private static final Set<String> DEPRECATED_SONAR_PROPERTIES = Set.of(
"sonar.jdbc.maxIdle",
"sonar.jdbc.minEvictableIdleTimeMillis",
"sonar.jdbc.timeBetweenEvictionRunsMillis");

private static final Set<String> IGNORED_SONAR_PROPERTIES = Set.of(SONAR_JDBC_DIALECT, JDBC_DRIVER_PATH.getKey(),
"sonar.jdbc.maxIdle",
"sonar.jdbc.minEvictableIdleTimeMillis",
"sonar.jdbc.timeBetweenEvictionRunsMillis");

private static final Map<String, String> SONAR_JDBC_TO_HIKARI_PROPERTY_MAPPINGS = Map.of(
JDBC_USERNAME.getKey(), "dataSource.user",
JDBC_PASSWORD.getKey(), "dataSource.password",
JDBC_EMBEDDED_PORT.getKey(), "dataSource.portNumber",
JDBC_URL.getKey(), "jdbcUrl",
SONAR_JDBC_MAX_WAIT, "connectionTimeout",
SONAR_JDBC_MAX_ACTIVE, "maximumPoolSize",
JDBC_MIN_IDLE.getKey(), "minimumIdle");

private final LogbackHelper logbackHelper;
private final Settings settings;
@@ -99,16 +117,22 @@ public class DefaultDatabase implements Database {
properties.setProperty(SONAR_JDBC_DRIVER, dialect.getDefaultDriverClassName());
}

private void initDataSource() throws Exception {
// but it's correctly caught by start()
LOG.info("Create JDBC data source for {}", properties.getProperty(JDBC_URL.getKey()), DEFAULT_URL);
BasicDataSource basicDataSource = BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties));
datasource = new ProfiledDataSource(basicDataSource, NullConnectionInterceptor.INSTANCE);
datasource.setConnectionInitSqls(dialect.getConnectionInitStatements());
datasource.setValidationQuery(dialect.getValidationQuery());
private void initDataSource() {
LOG.info("Create JDBC data source for {}", properties.getProperty(JDBC_URL.getKey(), DEFAULT_URL));
HikariDataSource ds = createHikariDataSource();
datasource = new ProfiledDataSource(ds, NullConnectionInterceptor.INSTANCE);
enableSqlLogging(datasource, logbackHelper.getLoggerLevel("sql") == Level.TRACE);
}

private HikariDataSource createHikariDataSource() {
HikariConfig config = new HikariConfig(extractCommonsHikariProperties(properties));
if (!dialect.getConnectionInitStatements().isEmpty()) {
config.setConnectionInitSql(dialect.getConnectionInitStatements().get(0));
}
config.setConnectionTestQuery(dialect.getValidationQuery());
return new HikariDataSource(config);
}

private void checkConnection() {
Connection connection = null;
try {
@@ -124,11 +148,7 @@ public class DefaultDatabase implements Database {
@Override
public void stop() {
if (datasource != null) {
try {
datasource.close();
} catch (SQLException e) {
throw new IllegalStateException("Fail to stop JDBC connection pool", e);
}
datasource.close();
}
}

@@ -171,17 +191,22 @@ public class DefaultDatabase implements Database {
}

@VisibleForTesting
static Properties extractCommonsDbcpProperties(Properties properties) {
static Properties extractCommonsHikariProperties(Properties properties) {
Properties result = new Properties();
result.setProperty("accessToUnderlyingConnectionAllowed", "true");
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = (String) entry.getKey();
if (IGNORED_SONAR_PROPERTIES.contains(key)) {
if (DEPRECATED_SONAR_PROPERTIES.contains(key)) {
LOG.warn("Property [{}] has no effect as pool connection implementation changed, check 9.7 upgrade notes.", key);
}
continue;
}
if (StringUtils.startsWith(key, SONAR_JDBC)) {
String resolvedKey = toDbcpPropertyKey(key);
String resolvedKey = toHikariPropertyKey(key);
String existingValue = (String) result.setProperty(resolvedKey, (String) entry.getValue());
checkState(existingValue == null || existingValue.equals(entry.getValue()),
"Duplicate property declaration for resolved jdbc key '%s': conflicting values are '%s' and '%s'", resolvedKey, existingValue, entry.getValue());
result.setProperty(toDbcpPropertyKey(key), (String) entry.getValue());
result.setProperty(resolvedKey, (String) entry.getValue());
}
}
return result;
@@ -193,9 +218,9 @@ public class DefaultDatabase implements Database {
}
}

private static String toDbcpPropertyKey(String key) {
if (SONAR_JDBC_TO_DBCP_PROPERTY_MAPPINGS.containsKey(key)) {
return SONAR_JDBC_TO_DBCP_PROPERTY_MAPPINGS.get(key);
private static String toHikariPropertyKey(String key) {
if (SONAR_JDBC_TO_HIKARI_PROPERTY_MAPPINGS.containsKey(key)) {
return SONAR_JDBC_TO_HIKARI_PROPERTY_MAPPINGS.get(key);
}

return StringUtils.removeStart(key, SONAR_JDBC);

+ 3
- 3
server/sonar-db-core/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java 파일 보기

@@ -21,12 +21,12 @@ package org.sonar.db.profiling;

import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp2.BasicDataSource;
import javax.sql.DataSource;

public interface ConnectionInterceptor {

Connection getConnection(BasicDataSource dataSource) throws SQLException;
Connection getConnection(DataSource dataSource) throws SQLException;

Connection getConnection(BasicDataSource dataSource, String login, String password) throws SQLException;
Connection getConnection(DataSource dataSource, String login, String password) throws SQLException;

}

+ 3
- 3
server/sonar-db-core/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java 파일 보기

@@ -21,18 +21,18 @@ package org.sonar.db.profiling;

import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp2.BasicDataSource;
import javax.sql.DataSource;

public enum NullConnectionInterceptor implements ConnectionInterceptor {
INSTANCE;

@Override
public Connection getConnection(BasicDataSource dataSource) throws SQLException {
public Connection getConnection(DataSource dataSource) throws SQLException {
return dataSource.getConnection();
}

@Override
public Connection getConnection(BasicDataSource dataSource, String user, String password) throws SQLException {
public Connection getConnection(DataSource dataSource, String user, String password) throws SQLException {
return dataSource.getConnection(user, password);
}
}

+ 3
- 3
server/sonar-db-core/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java 파일 보기

@@ -22,18 +22,18 @@ package org.sonar.db.profiling;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp2.BasicDataSource;
import javax.sql.DataSource;

public enum ProfiledConnectionInterceptor implements ConnectionInterceptor {
INSTANCE;

@Override
public Connection getConnection(BasicDataSource dataSource) throws SQLException {
public Connection getConnection(DataSource dataSource) throws SQLException {
return buildConnectionProxy(new ProfilingConnectionHandler(dataSource.getConnection()));
}

@Override
public Connection getConnection(BasicDataSource dataSource, String login, String password) throws SQLException {
public Connection getConnection(DataSource dataSource, String login, String password) throws SQLException {
return buildConnectionProxy(new ProfilingConnectionHandler(dataSource.getConnection(login, password)));
}


+ 191
- 310
server/sonar-db-core/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java 파일 보기

@@ -19,32 +19,37 @@
*/
package org.sonar.db.profiling;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariConfigMXBean;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.ConnectionBuilder;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.commons.dbcp2.BasicDataSource;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.ShardingKeyBuilder;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import javax.sql.DataSource;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;

public class ProfiledDataSource extends BasicDataSource {
public class ProfiledDataSource extends HikariDataSource {

static final Logger SQL_LOGGER = Loggers.get("sql");

private final BasicDataSource delegate;
private final HikariDataSource delegate;
private ConnectionInterceptor connectionInterceptor;

public ProfiledDataSource(BasicDataSource delegate, ConnectionInterceptor connectionInterceptor) {
public ProfiledDataSource(HikariDataSource delegate, ConnectionInterceptor connectionInterceptor) {
this.delegate = delegate;
this.connectionInterceptor = connectionInterceptor;
}

public BasicDataSource getDelegate() {
public HikariDataSource getDelegate() {
return delegate;
}

@@ -53,33 +58,33 @@ public class ProfiledDataSource extends BasicDataSource {
}

@Override
public Boolean getDefaultAutoCommit() {
return delegate.getDefaultAutoCommit();
public boolean isAutoCommit() {
return delegate.isAutoCommit();
}

@Override
public Boolean getDefaultReadOnly() {
return delegate.getDefaultReadOnly();
public boolean isReadOnly() {
return delegate.isReadOnly();
}

@Override
public int getDefaultTransactionIsolation() {
return delegate.getDefaultTransactionIsolation();
public String getTransactionIsolation() {
return delegate.getTransactionIsolation();
}

@Override
public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
delegate.setDefaultTransactionIsolation(defaultTransactionIsolation);
public void setTransactionIsolation(String defaultTransactionIsolation) {
delegate.setTransactionIsolation(defaultTransactionIsolation);
}

@Override
public String getDefaultCatalog() {
return delegate.getDefaultCatalog();
public String getCatalog() {
return delegate.getCatalog();
}

@Override
public void setDefaultCatalog(String defaultCatalog) {
delegate.setDefaultCatalog(defaultCatalog);
public void setCatalog(String defaultCatalog) {
delegate.setCatalog(defaultCatalog);
}

@Override
@@ -93,153 +98,158 @@ public class ProfiledDataSource extends BasicDataSource {
}

@Override
public synchronized ClassLoader getDriverClassLoader() {
return delegate.getDriverClassLoader();
public int getMaximumPoolSize() {
return delegate.getMaximumPoolSize();
}

@Override
public synchronized void setDriverClassLoader(ClassLoader driverClassLoader) {
delegate.setDriverClassLoader(driverClassLoader);
public void setMaximumPoolSize(int maxActive) {
delegate.setMaximumPoolSize(maxActive);
}

@Override
public synchronized int getMaxTotal() {
return delegate.getMaxTotal();
public Connection getConnection() throws SQLException {
return connectionInterceptor.getConnection(delegate);
}

@Override
public synchronized void setMaxTotal(int maxActive) {
delegate.setMaxTotal(maxActive);
public Connection getConnection(String login, String password) throws SQLException {
return connectionInterceptor.getConnection(this, login, password);
}

@Override
public synchronized int getMaxIdle() {
return delegate.getMaxIdle();
public PrintWriter getLogWriter() throws SQLException {
return delegate.getLogWriter();
}

@Override
public synchronized void setMaxIdle(int maxIdle) {
delegate.setMaxIdle(maxIdle);
public void setLogWriter(PrintWriter out) throws SQLException {
delegate.setLogWriter(out);
}

@Override
public synchronized int getMinIdle() {
return delegate.getMinIdle();
public void setLoginTimeout(int seconds) throws SQLException {
delegate.setLoginTimeout(seconds);
}

@Override
public synchronized void setMinIdle(int minIdle) {
delegate.setMinIdle(minIdle);
public int getLoginTimeout() throws SQLException {
return delegate.getLoginTimeout();
}

@Override
public synchronized int getInitialSize() {
return delegate.getInitialSize();
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
return delegate.getParentLogger();
}

@Override
public synchronized void setInitialSize(int initialSize) {
delegate.setInitialSize(initialSize);
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
}

@Override
public synchronized long getMaxWaitMillis() {
return delegate.getMaxWaitMillis();
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
}

@Override
public synchronized void setMaxWaitMillis(long maxWait) {
delegate.setMaxWaitMillis(maxWait);
public void setMetricRegistry(Object metricRegistry) {
delegate.setMetricRegistry(metricRegistry);
}

@Override
public synchronized boolean isPoolPreparedStatements() {
return delegate.isPoolPreparedStatements();
public void setMetricsTrackerFactory(MetricsTrackerFactory metricsTrackerFactory) {
delegate.setMetricsTrackerFactory(metricsTrackerFactory);
}

@Override
public synchronized void setPoolPreparedStatements(boolean poolingStatements) {
delegate.setPoolPreparedStatements(poolingStatements);
public void setHealthCheckRegistry(Object healthCheckRegistry) {
delegate.setHealthCheckRegistry(healthCheckRegistry);
}

@Override
public synchronized int getMaxOpenPreparedStatements() {
return delegate.getMaxOpenPreparedStatements();
public boolean isRunning() {
return delegate.isRunning();
}

@Override
public synchronized void setMaxOpenPreparedStatements(int maxOpenStatements) {
delegate.setMaxOpenPreparedStatements(maxOpenStatements);
public HikariPoolMXBean getHikariPoolMXBean() {
return delegate.getHikariPoolMXBean();
}

@Override
public synchronized boolean getTestOnBorrow() {
return delegate.getTestOnBorrow();
public HikariConfigMXBean getHikariConfigMXBean() {
return delegate.getHikariConfigMXBean();
}

@Override
public synchronized void setTestOnBorrow(boolean testOnBorrow) {
delegate.setTestOnBorrow(testOnBorrow);
public void evictConnection(Connection connection) {
delegate.evictConnection(connection);
}

@Override
public synchronized boolean getTestOnReturn() {
return delegate.getTestOnReturn();
public void close() {
delegate.close();
}

@Override
public synchronized void setTestOnReturn(boolean testOnReturn) {
delegate.setTestOnReturn(testOnReturn);
public boolean isClosed() {
return delegate.isClosed();
}

@Override
public synchronized long getTimeBetweenEvictionRunsMillis() {
return delegate.getTimeBetweenEvictionRunsMillis();
public String toString() {
return delegate.toString();
}

@Override
public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
delegate.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
public long getConnectionTimeout() {
return delegate.getConnectionTimeout();
}

@Override
public synchronized int getNumTestsPerEvictionRun() {
return delegate.getNumTestsPerEvictionRun();
public void setConnectionTimeout(long connectionTimeoutMs) {
delegate.setConnectionTimeout(connectionTimeoutMs);
}

@Override
public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
delegate.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
public long getIdleTimeout() {
return delegate.getIdleTimeout();
}

@Override
public synchronized long getMinEvictableIdleTimeMillis() {
return delegate.getMinEvictableIdleTimeMillis();
public void setIdleTimeout(long idleTimeoutMs) {
delegate.setIdleTimeout(idleTimeoutMs);
}

@Override
public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
delegate.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
public long getLeakDetectionThreshold() {
return delegate.getLeakDetectionThreshold();
}

@Override
public synchronized boolean getTestWhileIdle() {
return delegate.getTestWhileIdle();
public void setLeakDetectionThreshold(long leakDetectionThresholdMs) {
delegate.setLeakDetectionThreshold(leakDetectionThresholdMs);
}

@Override
public synchronized void setTestWhileIdle(boolean testWhileIdle) {
delegate.setTestWhileIdle(testWhileIdle);
public long getMaxLifetime() {
return delegate.getMaxLifetime();
}

@Override
public synchronized int getNumActive() {
return delegate.getNumActive();
public void setMaxLifetime(long maxLifetimeMs) {
delegate.setMaxLifetime(maxLifetimeMs);
}

@Override
public synchronized int getNumIdle() {
return delegate.getNumIdle();
public int getMinimumIdle() {
return delegate.getMinimumIdle();
}

@Override
public void setMinimumIdle(int minIdle) {
delegate.setMinimumIdle(minIdle);
}

@Override
@@ -252,16 +262,6 @@ public class ProfiledDataSource extends BasicDataSource {
delegate.setPassword(password);
}

@Override
public synchronized String getUrl() {
return delegate.getUrl();
}

@Override
public synchronized void setUrl(String url) {
delegate.setUrl(url);
}

@Override
public String getUsername() {
return delegate.getUsername();
@@ -273,368 +273,249 @@ public class ProfiledDataSource extends BasicDataSource {
}

@Override
public String getValidationQuery() {
return delegate.getValidationQuery();
}

@Override
public void setValidationQuery(String validationQuery) {
delegate.setValidationQuery(validationQuery);
}

@Override
public int getValidationQueryTimeout() {
return delegate.getValidationQueryTimeout();
}

@Override
public void setValidationQueryTimeout(int timeout) {
delegate.setValidationQueryTimeout(timeout);
}

@Override
public List<String> getConnectionInitSqls() {
return delegate.getConnectionInitSqls();
}

@Override
public void setConnectionInitSqls(Collection<String> connectionInitSqls) {
delegate.setConnectionInitSqls(connectionInitSqls);
}

@Override
public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
return delegate.isAccessToUnderlyingConnectionAllowed();
}

@Override
public synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
delegate.setAccessToUnderlyingConnectionAllowed(allow);
}

@Override
public Connection getConnection() throws SQLException {
return connectionInterceptor.getConnection(delegate);
}

@Override
public Connection getConnection(String login, String password) throws SQLException {
return connectionInterceptor.getConnection(this, login, password);
}

@Override
public int getLoginTimeout() throws SQLException {
return delegate.getLoginTimeout();
}

@Override
public java.util.logging.Logger getParentLogger() {
return java.util.logging.Logger.getLogger(getClass().getName());
}

@Override
public PrintWriter getLogWriter() throws SQLException {
return delegate.getLogWriter();
}

@Override
public void setLoginTimeout(int loginTimeout) throws SQLException {
delegate.setLoginTimeout(loginTimeout);
}

@Override
public void setLogWriter(PrintWriter logWriter) throws SQLException {
delegate.setLogWriter(logWriter);
}

@Override
public boolean getRemoveAbandonedOnBorrow() {
return delegate.getRemoveAbandonedOnBorrow();
}

@Override
public void setRemoveAbandonedOnBorrow(boolean removeAbandoned) {
delegate.setRemoveAbandonedOnBorrow(removeAbandoned);
}


@Override
public boolean getRemoveAbandonedOnMaintenance() {
return delegate.getRemoveAbandonedOnMaintenance();
public long getValidationTimeout() {
return delegate.getValidationTimeout();
}

@Override
public void setRemoveAbandonedOnMaintenance(boolean removeAbandoned) {
delegate.setRemoveAbandonedOnMaintenance(removeAbandoned);
public void setValidationTimeout(long validationTimeoutMs) {
delegate.setValidationTimeout(validationTimeoutMs);
}

@Override
public int getRemoveAbandonedTimeout() {
return delegate.getRemoveAbandonedTimeout();
public String getConnectionTestQuery() {
return delegate.getConnectionTestQuery();
}

@Override
public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
delegate.setRemoveAbandonedTimeout(removeAbandonedTimeout);
public void setConnectionTestQuery(String connectionTestQuery) {
delegate.setConnectionTestQuery(connectionTestQuery);
}

@Override
public boolean getLogAbandoned() {
return delegate.getLogAbandoned();
public String getConnectionInitSql() {
return delegate.getConnectionInitSql();
}

@Override
public void setLogAbandoned(boolean logAbandoned) {
delegate.setLogAbandoned(logAbandoned);
public void setConnectionInitSql(String connectionInitSql) {
delegate.setConnectionInitSql(connectionInitSql);
}

@Override
public void addConnectionProperty(String name, String value) {
delegate.addConnectionProperty(name, value);
public DataSource getDataSource() {
return delegate.getDataSource();
}

@Override
public void removeConnectionProperty(String name) {
delegate.removeConnectionProperty(name);
public void setDataSource(DataSource dataSource) {
delegate.setDataSource(dataSource);
}

@Override
public void setConnectionProperties(String connectionProperties) {
delegate.setConnectionProperties(connectionProperties);
public String getDataSourceClassName() {
return delegate.getDataSourceClassName();
}

@Override
public synchronized void close() throws SQLException {
delegate.close();
}

@Override
public synchronized boolean isClosed() {
return delegate.isClosed();
public void setDataSourceClassName(String className) {
delegate.setDataSourceClassName(className);
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
public void addDataSourceProperty(String propertyName, Object value) {
delegate.addDataSourceProperty(propertyName, value);
}

@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
public String getDataSourceJNDI() {
return delegate.getDataSourceJNDI();
}

@Override
public void setDefaultAutoCommit(Boolean defaultAutoCommit) {
delegate.setDefaultAutoCommit(defaultAutoCommit);
public void setDataSourceJNDI(String jndiDataSource) {
delegate.setDataSourceJNDI(jndiDataSource);
}

@Override
public void setDefaultReadOnly(Boolean defaultReadOnly) {
delegate.setDefaultReadOnly(defaultReadOnly);
public Properties getDataSourceProperties() {
return delegate.getDataSourceProperties();
}

@Override
public Integer getDefaultQueryTimeout() {
return delegate.getDefaultQueryTimeout();
public void setDataSourceProperties(Properties dsProperties) {
delegate.setDataSourceProperties(dsProperties);
}

@Override
public void setDefaultQueryTimeout(Integer defaultQueryTimeoutSeconds) {
delegate.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
public String getJdbcUrl() {
return delegate.getJdbcUrl();
}

@Override
public String getDefaultSchema() {
return delegate.getDefaultSchema();
public void setJdbcUrl(String jdbcUrl) {
delegate.setJdbcUrl(jdbcUrl);
}

@Override
public void setDefaultSchema(String defaultSchema) {
delegate.setDefaultSchema(defaultSchema);
public void setAutoCommit(boolean isAutoCommit) {
delegate.setAutoCommit(isAutoCommit);
}

@Override
public boolean getCacheState() {
return delegate.getCacheState();
public boolean isAllowPoolSuspension() {
return delegate.isAllowPoolSuspension();
}

@Override
public void setCacheState(boolean cacheState) {
delegate.setCacheState(cacheState);
public void setAllowPoolSuspension(boolean isAllowPoolSuspension) {
delegate.setAllowPoolSuspension(isAllowPoolSuspension);
}

@Override
public synchronized Driver getDriver() {
return delegate.getDriver();
public long getInitializationFailTimeout() {
return delegate.getInitializationFailTimeout();
}

@Override
public synchronized void setDriver(Driver driver) {
delegate.setDriver(driver);
public void setInitializationFailTimeout(long initializationFailTimeout) {
delegate.setInitializationFailTimeout(initializationFailTimeout);
}

@Override
public synchronized boolean getLifo() {
return delegate.getLifo();
public boolean isIsolateInternalQueries() {
return delegate.isIsolateInternalQueries();
}

@Override
public synchronized void setLifo(boolean lifo) {
delegate.setLifo(lifo);
public void setIsolateInternalQueries(boolean isolate) {
delegate.setIsolateInternalQueries(isolate);
}

@Override
public synchronized boolean getTestOnCreate() {
return delegate.getTestOnCreate();
public MetricsTrackerFactory getMetricsTrackerFactory() {
return delegate.getMetricsTrackerFactory();
}

@Override
public synchronized void setTestOnCreate(boolean testOnCreate) {
delegate.setTestOnCreate(testOnCreate);
public Object getMetricRegistry() {
return delegate.getMetricRegistry();
}

@Override
public synchronized void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) {
delegate.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
public Object getHealthCheckRegistry() {
return delegate.getHealthCheckRegistry();
}

@Override
public synchronized long getSoftMinEvictableIdleTimeMillis() {
return delegate.getSoftMinEvictableIdleTimeMillis();
public Properties getHealthCheckProperties() {
return delegate.getHealthCheckProperties();
}

@Override
public synchronized String getEvictionPolicyClassName() {
return delegate.getEvictionPolicyClassName();
public void setHealthCheckProperties(Properties healthCheckProperties) {
delegate.setHealthCheckProperties(healthCheckProperties);
}

@Override
public synchronized void setEvictionPolicyClassName(String evictionPolicyClassName) {
delegate.setEvictionPolicyClassName(evictionPolicyClassName);
public void addHealthCheckProperty(String key, String value) {
delegate.addHealthCheckProperty(key, value);
}

@Override
public String[] getConnectionInitSqlsAsArray() {
return delegate.getConnectionInitSqlsAsArray();
public long getKeepaliveTime() {
return delegate.getKeepaliveTime();
}

@Override
public long getMaxConnLifetimeMillis() {
return delegate.getMaxConnLifetimeMillis();
public void setKeepaliveTime(long keepaliveTimeMs) {
delegate.setKeepaliveTime(keepaliveTimeMs);
}

@Override
public boolean getLogExpiredConnections() {
return delegate.getLogExpiredConnections();
public void setReadOnly(boolean readOnly) {
delegate.setReadOnly(readOnly);
}

@Override
public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
delegate.setMaxConnLifetimeMillis(maxConnLifetimeMillis);
public boolean isRegisterMbeans() {
return delegate.isRegisterMbeans();
}

@Override
public void setLogExpiredConnections(boolean logExpiredConnections) {
delegate.setLogExpiredConnections(logExpiredConnections);
public void setRegisterMbeans(boolean register) {
delegate.setRegisterMbeans(register);
}

@Override
public String getJmxName() {
return delegate.getJmxName();
public String getPoolName() {
return delegate.getPoolName();
}

@Override
public void setJmxName(String jmxName) {
delegate.setJmxName(jmxName);
public void setPoolName(String poolName) {
delegate.setPoolName(poolName);
}

@Override
public boolean getEnableAutoCommitOnReturn() {
return delegate.getEnableAutoCommitOnReturn();
public ScheduledExecutorService getScheduledExecutor() {
return delegate.getScheduledExecutor();
}

@Override
public void setEnableAutoCommitOnReturn(boolean enableAutoCommitOnReturn) {
delegate.setEnableAutoCommitOnReturn(enableAutoCommitOnReturn);
public void setScheduledExecutor(ScheduledExecutorService executor) {
delegate.setScheduledExecutor(executor);
}

@Override
public boolean getRollbackOnReturn() {
return delegate.getRollbackOnReturn();
public String getSchema() {
return delegate.getSchema();
}

@Override
public void setRollbackOnReturn(boolean rollbackOnReturn) {
delegate.setRollbackOnReturn(rollbackOnReturn);
public void setSchema(String schema) {
delegate.setSchema(schema);
}

@Override
public Set<String> getDisconnectionSqlCodes() {
return delegate.getDisconnectionSqlCodes();
public String getExceptionOverrideClassName() {
return delegate.getExceptionOverrideClassName();
}

@Override
public String[] getDisconnectionSqlCodesAsArray() {
return delegate.getDisconnectionSqlCodesAsArray();
public void setExceptionOverrideClassName(String exceptionOverrideClassName) {
delegate.setExceptionOverrideClassName(exceptionOverrideClassName);
}

@Override
public void setDisconnectionSqlCodes(Collection<String> disconnectionSqlCodes) {
delegate.setDisconnectionSqlCodes(disconnectionSqlCodes);
public ThreadFactory getThreadFactory() {
return delegate.getThreadFactory();
}

@Override
public boolean getFastFailValidation() {
return delegate.getFastFailValidation();
public void setThreadFactory(ThreadFactory threadFactory) {
delegate.setThreadFactory(threadFactory);
}

@Override
public void setFastFailValidation(boolean fastFailValidation) {
delegate.setFastFailValidation(fastFailValidation);
public void copyStateTo(HikariConfig other) {
delegate.copyStateTo(other);
}

@Override
public PrintWriter getAbandonedLogWriter() {
return delegate.getAbandonedLogWriter();
public void validate() {
delegate.validate();
}

@Override
public void setAbandonedLogWriter(PrintWriter logWriter) {
delegate.setAbandonedLogWriter(logWriter);
public ConnectionBuilder createConnectionBuilder() throws SQLException {
return delegate.createConnectionBuilder();
}

@Override
public boolean getAbandonedUsageTracking() {
return delegate.getAbandonedUsageTracking();
public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException {
return delegate.createShardingKeyBuilder();
}

@Override
public void setAbandonedUsageTracking(boolean usageTracking) {
delegate.setAbandonedUsageTracking(usageTracking);
}

@Override
public void invalidateConnection(Connection connection) {
delegate.invalidateConnection(connection);
}

@Override
public ObjectName preRegister(MBeanServer server, ObjectName objectName) {
return delegate.preRegister(server, objectName);
}

@Override
public void postRegister(Boolean registrationDone) {
delegate.postRegister(registrationDone);
}

@Override
public void preDeregister() throws Exception {
delegate.preDeregister();
}

@Override
public void postDeregister() {
delegate.postDeregister();
}
}

+ 40
- 19
server/sonar-db-core/src/test/java/org/sonar/db/DefaultDatabaseTest.java 파일 보기

@@ -22,11 +22,13 @@ package org.sonar.db;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import com.zaxxer.hikari.HikariDataSource;
import java.util.Properties;
import org.apache.commons.dbcp2.BasicDataSource;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.log.LogTester;
import org.sonar.db.dialect.PostgreSql;
import org.sonar.process.logging.LogbackHelper;

@@ -38,9 +40,11 @@ import static org.mockito.Mockito.mock;
@RunWith(DataProviderRunner.class)
public class DefaultDatabaseTest {

private LogbackHelper logbackHelper = mock(LogbackHelper.class);
private final LogbackHelper logbackHelper = mock(LogbackHelper.class);
private static final String SONAR_JDBC = "sonar.jdbc.";

@Rule
public final LogTester logTester = new LogTester();

@Test
public void shouldLoadDefaultValues() {
@@ -54,19 +58,36 @@ public class DefaultDatabaseTest {
}

@Test
public void shouldExtractCommonsDbcpProperties() {
public void shouldExtractHikariProperties() {
Properties props = new Properties();
props.setProperty("sonar.jdbc.driverClassName", "my.Driver");
props.setProperty("sonar.jdbc.username", "me");
props.setProperty("sonar.jdbc.maxActive", "5");
props.setProperty("sonar.jdbc.maxWait", "5000");
props.setProperty("sonar.jdbc.maximumPoolSize", "5");
props.setProperty("sonar.jdbc.connectionTimeout", "5000");

Properties commonsDbcpProps = DefaultDatabase.extractCommonsDbcpProperties(props);
Properties hikariProps = DefaultDatabase.extractCommonsHikariProperties(props);

assertThat(commonsDbcpProps.getProperty("username")).isEqualTo("me");
assertThat(commonsDbcpProps.getProperty("driverClassName")).isEqualTo("my.Driver");
assertThat(commonsDbcpProps.getProperty("maxTotal")).isEqualTo("5");
assertThat(commonsDbcpProps.getProperty("maxWaitMillis")).isEqualTo("5000");
assertThat(hikariProps.getProperty("dataSource.user")).isEqualTo("me");
assertThat(hikariProps.getProperty("driverClassName")).isEqualTo("my.Driver");
assertThat(hikariProps.getProperty("maximumPoolSize")).isEqualTo("5");
assertThat(hikariProps.getProperty("connectionTimeout")).isEqualTo("5000");
}

@Test
public void logWarningIfDeprecatedPropertyUsed() {
Properties props = new Properties();

props.setProperty("sonar.jdbc.maxIdle", "5");
props.setProperty("sonar.jdbc.minEvictableIdleTimeMillis", "300000");
props.setProperty("sonar.jdbc.timeBetweenEvictionRunsMillis", "1000");
props.setProperty("sonar.jdbc.connectionTimeout", "8000");

DefaultDatabase.extractCommonsHikariProperties(props);

assertThat(logTester.logs())
.contains("Property [sonar.jdbc.maxIdle] has no effect as pool connection implementation changed, check 9.7 upgrade notes.")
.contains("Property [sonar.jdbc.minEvictableIdleTimeMillis] has no effect as pool connection implementation changed, check 9.7 upgrade notes.")
.contains("Property [sonar.jdbc.timeBetweenEvictionRunsMillis] has no effect as pool connection implementation changed, check 9.7 upgrade notes.");
}

@Test
@@ -76,21 +97,21 @@ public class DefaultDatabaseTest {
props.setProperty(jdbcProperty, "100");
props.setProperty(dbcpProperty, "100");

Properties commonsDbcpProps = DefaultDatabase.extractCommonsDbcpProperties(props);
Properties commonsDbcpProps = DefaultDatabase.extractCommonsHikariProperties(props);

assertThat(commonsDbcpProps.getProperty(removeStart(dbcpProperty, SONAR_JDBC))).isEqualTo("100");
}

@Test
@UseDataProvider("sonarJdbcAndDbcpProperties")
public void shouldThrowISEIfDuplicatedResolvedPropertiesWithDifferentValue(String jdbcProperty, String dbcpProperty) {
public void shouldThrowISEIfDuplicatedResolvedPropertiesWithDifferentValue(String jdbcProperty, String hikariProperty) {
Properties props = new Properties();
props.setProperty(jdbcProperty, "100");
props.setProperty(dbcpProperty, "200");
props.setProperty(hikariProperty, "200");

assertThatThrownBy(() -> DefaultDatabase.extractCommonsDbcpProperties(props))
assertThatThrownBy(() -> DefaultDatabase.extractCommonsHikariProperties(props))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining(String.format("Duplicate property declaration for resolved jdbc key '%s': conflicting values are", removeStart(dbcpProperty, SONAR_JDBC)));
.hasMessageContaining(String.format("Duplicate property declaration for resolved jdbc key '%s': conflicting values are", removeStart(hikariProperty, SONAR_JDBC)));
}

@Test
@@ -117,14 +138,14 @@ public class DefaultDatabaseTest {
settings.setProperty("sonar.jdbc.driverClassName", "org.h2.Driver");
settings.setProperty("sonar.jdbc.username", "sonar");
settings.setProperty("sonar.jdbc.password", "sonar");
settings.setProperty("sonar.jdbc.maxActive", "1");
settings.setProperty("sonar.jdbc.maximumPoolSize", "1");

DefaultDatabase db = new DefaultDatabase(logbackHelper, settings);
db.start();
db.stop();

assertThat(db.getDialect().getId()).isEqualTo("h2");
assertThat(((BasicDataSource) db.getDataSource()).getMaxTotal()).isOne();
assertThat(((HikariDataSource) db.getDataSource()).getMaximumPoolSize()).isOne();
}

@Test
@@ -152,8 +173,8 @@ public class DefaultDatabaseTest {
@DataProvider
public static Object[][] sonarJdbcAndDbcpProperties() {
return new Object[][] {
{"sonar.jdbc.maxActive", "sonar.jdbc.maxTotal"},
{"sonar.jdbc.maxWait", "sonar.jdbc.maxWaitMillis"}
{"sonar.jdbc.maxWait", "sonar.jdbc.connectionTimeout"},
{"sonar.jdbc.maxActive", "sonar.jdbc.maximumPoolSize"}
};
}
}

+ 1
- 10
server/sonar-db-core/src/test/java/org/sonar/db/profiling/InvocationUtilsTest.java 파일 보기

@@ -23,6 +23,7 @@ import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.sonar.test.TestUtils;

@@ -51,16 +52,6 @@ public class InvocationUtilsTest {
Assert.assertThrows(SQLException.class, () -> InvocationUtils.invokeQuietly(target, prepareStatement, new Object[] {failSql}));
}

@Test
public void should_wrap_undeclared_exception() throws Throwable {
Connection target = mock(Connection.class);
String failSql = "any sql";
when(target.prepareStatement(failSql)).thenThrow(new SQLException("Expected"));
Method wait = Object.class.getMethod("wait");

Assert.assertThrows(IllegalStateException.class, () -> InvocationUtils.invokeQuietly(target, wait, new Object[0]));
}

@Test
public void only_static_methods() {
assertThat(TestUtils.hasOnlyPrivateConstructors(InvocationUtils.class)).isTrue();

+ 41
- 5
server/sonar-db-core/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java 파일 보기

@@ -19,6 +19,8 @@
*/
package org.sonar.db.profiling;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.io.ByteArrayInputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -27,7 +29,9 @@ import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.sql.Timestamp;
import org.apache.commons.dbcp2.BasicDataSource;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.log.LogTester;
@@ -36,6 +40,7 @@ import org.sonar.api.utils.log.LoggerLevel;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class ProfiledDataSourceTest {
@@ -43,7 +48,7 @@ public class ProfiledDataSourceTest {
@Rule
public LogTester logTester = new LogTester();

BasicDataSource originDataSource = mock(BasicDataSource.class);
HikariDataSource originDataSource = mock(HikariDataSource.class);

@Test
public void execute_and_log_statement() throws Exception {
@@ -59,7 +64,7 @@ public class ProfiledDataSourceTest {

ProfiledDataSource underTest = new ProfiledDataSource(originDataSource, ProfiledConnectionInterceptor.INSTANCE);

assertThat(underTest.getUrl()).isNull();
assertThat(underTest.getJdbcUrl()).isNull();
assertThat(underTest.getConnection().getClientInfo()).isNull();
final Statement statementProxy = underTest.getConnection().createStatement();
assertThat(statementProxy.getConnection()).isNull();
@@ -90,7 +95,7 @@ public class ProfiledDataSourceTest {

ProfiledDataSource ds = new ProfiledDataSource(originDataSource, ProfiledConnectionInterceptor.INSTANCE);

assertThat(ds.getUrl()).isNull();
assertThat(ds.getJdbcUrl()).isNull();
assertThat(ds.getConnection().getClientInfo()).isNull();
PreparedStatement preparedStatementProxy = ds.getConnection().prepareStatement(sqlWithParams);
preparedStatementProxy.setInt(1, param1);
@@ -121,7 +126,7 @@ public class ProfiledDataSourceTest {

ProfiledDataSource ds = new ProfiledDataSource(originDataSource, ProfiledConnectionInterceptor.INSTANCE);

assertThat(ds.getUrl()).isNull();
assertThat(ds.getJdbcUrl()).isNull();
assertThat(ds.getConnection().getClientInfo()).isNull();
PreparedStatement preparedStatementProxy = ds.getConnection().prepareStatement(sqlWithParams);
assertThat(preparedStatementProxy.getConnection()).isNull();
@@ -144,7 +149,38 @@ public class ProfiledDataSourceTest {
for (Method method : ProfiledDataSource.class.getDeclaredMethods()) {
if (method.getParameterTypes().length == 0 && Modifier.isPublic(method.getModifiers())) {
method.invoke(proxy);
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(String.class) && Modifier.isPublic(method.getModifiers())) {
method.invoke(proxy, "test");
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(Boolean.TYPE) && Modifier.isPublic(method.getModifiers())) {
method.invoke(proxy, true);
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(Long.TYPE) && Modifier.isPublic(method.getModifiers())) {
method.invoke(proxy, 1L);
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(Integer.TYPE) && Modifier.isPublic(method.getModifiers())) {
method.invoke(proxy, 1);
}
}

proxy.addHealthCheckProperty("test", "test");
verify(originDataSource).addHealthCheckProperty("test", "test");

var schedulerMock = mock(ScheduledExecutorService.class);
proxy.setScheduledExecutor(schedulerMock);
verify(originDataSource).setScheduledExecutor(schedulerMock);

var hikariConfigMock = mock(HikariConfig.class);
proxy.copyStateTo(hikariConfigMock);
verify(originDataSource).copyStateTo(hikariConfigMock);

var threadFactoryMock = mock(ThreadFactory.class);
proxy.setThreadFactory(threadFactoryMock);
verify(originDataSource).setThreadFactory(threadFactoryMock);

var properties = new Properties();
proxy.setHealthCheckProperties(properties);
verify(originDataSource).setHealthCheckProperties(properties);

proxy.setDataSourceProperties(properties);
verify(originDataSource).setDataSourceProperties(properties);

}
}

+ 5
- 9
server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreH2Database.java 파일 보기

@@ -19,11 +19,11 @@
*/
package org.sonar.db;

import com.zaxxer.hikari.HikariDataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.io.output.NullWriter;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
@@ -38,7 +38,7 @@ import static java.lang.String.format;
public class CoreH2Database implements Database {
private static final String IGNORED_KEYWORDS_OPTION = ";NON_KEYWORDS=VALUE";
private final String name;
private BasicDataSource datasource;
private HikariDataSource datasource;

/**
* IMPORTANT: change DB name in order to not conflict with {@link DefaultDatabaseTest}
@@ -54,11 +54,11 @@ public class CoreH2Database implements Database {

private void startDatabase() {
try {
datasource = new BasicDataSource();
datasource = new HikariDataSource();
datasource.setDriverClassName("org.h2.Driver");
datasource.setUsername("sonar");
datasource.setPassword("sonar");
datasource.setUrl("jdbc:h2:mem:" + name);
datasource.setJdbcUrl("jdbc:h2:mem:" + name);
} catch (Exception e) {
throw new IllegalStateException("Fail to start H2", e);
}
@@ -93,11 +93,7 @@ public class CoreH2Database implements Database {

@Override
public void stop() {
try {
datasource.close();
} catch (SQLException e) {
// Ignore error
}
datasource.close();
}

public DataSource getDataSource() {

+ 75
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/DatabaseMBean.java 파일 보기

@@ -0,0 +1,75 @@
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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;

import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import java.util.function.Function;
import org.sonar.process.systeminfo.BaseSectionMBean;

import static java.util.Optional.ofNullable;

public abstract class DatabaseMBean extends BaseSectionMBean {

private final DbClient dbClient;

protected DatabaseMBean(DbClient dbClient) {
this.dbClient = dbClient;
}

public int getPoolActiveConnections() {
return getIntPropertyOrZero(HikariPoolMXBean::getActiveConnections);
}

public int getPoolTotalConnections() {
return getIntPropertyOrZero(HikariPoolMXBean::getTotalConnections);
}

public int getPoolIdleConnections() {
return getIntPropertyOrZero(HikariPoolMXBean::getIdleConnections);
}

public int getPoolMaxConnections() {
return hikariDatasource().getHikariConfigMXBean().getMaximumPoolSize();
}

public int getPoolMinIdleConnections() {
return hikariDatasource().getHikariConfigMXBean().getMinimumIdle();
}

public long getPoolMaxLifeTimeMillis() {
return hikariDatasource().getHikariConfigMXBean().getMaxLifetime();
}

public long getPoolMaxWaitMillis() {
return hikariDatasource().getHikariConfigMXBean().getConnectionTimeout();
}

private HikariDataSource hikariDatasource() {
return (HikariDataSource) dbClient.getDatabase().getDataSource();
}

private int getIntPropertyOrZero(Function<HikariPoolMXBean, Integer> function) {
return ofNullable(hikariDatasource().getHikariPoolMXBean())
.map(function)
.orElse(0);
}

}

+ 65
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/DatabaseMBeanTest.java 파일 보기

@@ -0,0 +1,65 @@
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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;

import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Section;

import static org.assertj.core.api.Assertions.assertThat;

public class DatabaseMBeanTest {

@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);

private final FakeDatabaseMBean underTest = new FakeDatabaseMBean(dbTester.getDbClient());

@Test
public void verify_mBean() {

assertThat(underTest.getPoolActiveConnections()).isNotNegative();
assertThat(underTest.getPoolIdleConnections()).isNotNegative();
assertThat(underTest.getPoolMaxConnections()).isNotNegative();
assertThat(underTest.getPoolMaxLifeTimeMillis()).isNotNegative();
assertThat(underTest.getPoolMinIdleConnections()).isNotNegative();
assertThat(underTest.getPoolTotalConnections()).isNotNegative();
assertThat(underTest.getPoolMaxWaitMillis()).isNotNegative();
assertThat(underTest.name()).isEqualTo("FakeMBean");
}

private static class FakeDatabaseMBean extends DatabaseMBean {

protected FakeDatabaseMBean(DbClient dbClient) {
super(dbClient);
}

@Override
protected String name() {
return "FakeMBean";
}

@Override
public Section toProtobuf() {
return Section.newBuilder().build();
}
}
}

+ 2
- 2
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/DbTester.java 파일 보기

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

import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
@@ -27,7 +28,6 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.apache.commons.dbcp2.BasicDataSource;
import org.sonar.api.utils.System2;
import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.core.util.UuidFactory;
@@ -308,7 +308,7 @@ public class DbTester extends AbstractDbTester<TestDbImpl> {
}

public String getUrl() {
return ((BasicDataSource) db.getDatabase().getDataSource()).getUrl();
return ((HikariDataSource) db.getDatabase().getDataSource()).getJdbcUrl();
}

private static class DbSessionConnectionSupplier implements ConnectionSupplier {

+ 4
- 7
server/sonar-docs/src/pages/instance-administration/monitoring.md 파일 보기

@@ -72,14 +72,11 @@ All these MBeans are read-only. It's not possible to modify or reset their value
| Attribute Name | Description
| ---|---
| MigrationStatus | Possible values are: UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL (only available for WebServer).
| PoolActiveConnections | Number of active database connections
| PoolActiveConnections | Number of active database connections
| PoolIdleConnections | Number of database connections waiting to be used
| PoolInitialSize | Initial size of the database connections pool.
| PoolMaxActiveConnections | Maximum number of active database connections
| PoolMaxIdleConnections | Maximum number of database connections waiting to be used
| PoolMaxWaitMillis | In milliseconds
| PoolRemoveAbandoned | Possible values : true, false
| PoolRemoveAbandonedTimeoutSeconds | In Seconds
| PoolMaxConnections | Maximum number of active database connections
| PoolTotalConnections | Total number of connections currently in the pool
| PoolMaxWaitMillis | Maximum number of milliseconds that a client will wait for a connection from the pool

[[collapse]]
| ## SonarQube MBean

+ 5
- 0
server/sonar-docs/src/pages/setup/upgrade-notes.md 파일 보기

@@ -3,6 +3,11 @@ title: Release Upgrade Notes
url: /setup/upgrade-notes/
---

## Release 9.7 Upgrade notes

**Database Connection Pool Changed**
Database connection pool has been changed, following properties will no longer have effect: sonar.jdbc.maxIdle, sonar.jdbc.minEvictableIdleTimeMillis, sonar.jdbc.timeBetweenEvictionRunsMillis. See documentation for changes in [monitoring database connection pool](/instance-administration/monitoring/).

## Release 9.6 Upgrade notes
**Microsoft SQL Server changes in configuration and Integrated Authentication**
* If your Microsoft SQL Server doesn't support encryption, you will need to add `encrypt=false` to the JDBC URL connection string. ([SONAR-16249](https://jira.sonarsource.com/browse/SONAR-16249)).

+ 2
- 5
server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java 파일 보기

@@ -55,11 +55,8 @@ public class ProcessProperties {
JDBC_PASSWORD("sonar.jdbc.password", ""),
JDBC_DRIVER_PATH("sonar.jdbc.driverPath"),
JDBC_MAX_ACTIVE("sonar.jdbc.maxActive", "60"),
JDBC_MAX_IDLE("sonar.jdbc.maxIdle", "5"),
JDBC_MIN_IDLE("sonar.jdbc.minIdle", "2"),
JDBC_MAX_WAIT("sonar.jdbc.maxWait", "5000"),
JDBC_MIN_EVICTABLE_IDLE_TIME_MILLIS("sonar.jdbc.minEvictableIdleTimeMillis", "600000"),
JDBC_TIME_BETWEEN_EVICTION_RUNS_MILLIS("sonar.jdbc.timeBetweenEvictionRunsMillis", "30000"),
JDBC_MIN_IDLE("sonar.jdbc.minIdle", "10"),
JDBC_MAX_WAIT("sonar.jdbc.maxWait", "8000"),
JDBC_EMBEDDED_PORT("sonar.embeddedDatabase.port"),

PATH_DATA("sonar.path.data", "data"),

server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/BaseSectionMBean.java → server/sonar-process/src/main/java/org/sonar/process/systeminfo/BaseSectionMBean.java 파일 보기

@@ -17,11 +17,10 @@
* 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.server.platform.monitoring;
package org.sonar.process.systeminfo;

import org.sonar.api.Startable;
import org.sonar.process.Jmx;
import org.sonar.process.systeminfo.SystemInfoSection;

/**
* Base implementation of a {@link SystemInfoSection}
@@ -45,12 +44,12 @@ public abstract class BaseSectionMBean implements SystemInfoSection, Startable {
Jmx.unregister(objectName());
}

String objectName() {
public String objectName() {
return "SonarQube:name=" + name();
}

/**
* Name of section in System Info page
*/
abstract String name();
protected abstract String name();
}

+ 79
- 0
server/sonar-process/src/test/java/org/sonar/process/systeminfo/BaseSectionMBeanTest.java 파일 보기

@@ -0,0 +1,79 @@
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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.process.systeminfo;

import java.lang.management.ManagementFactory;
import javax.annotation.CheckForNull;
import javax.management.InstanceNotFoundException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.junit.Test;
import org.sonar.process.jmx.FakeMBean;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Section;

import static org.assertj.core.api.Assertions.assertThat;

public class BaseSectionMBeanTest {

private static final String EXPECTED_NAME = "SonarQube:name=FakeName";

private final FakeBaseSectionMBean underTest = new FakeBaseSectionMBean();

@Test
public void verify_mbean() throws Exception {
assertThat(lookupMBean()).isNull();

underTest.start();
assertThat(lookupMBean()).isNotNull();

underTest.stop();
assertThat(lookupMBean()).isNull();

assertThat(underTest.name()).isEqualTo("FakeName");
assertThat(underTest.toProtobuf()).isNotNull();
}

@CheckForNull
private ObjectInstance lookupMBean() throws Exception {
try {
return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(EXPECTED_NAME));
} catch (InstanceNotFoundException e) {
return null;
}
}

private static class FakeBaseSectionMBean extends BaseSectionMBean implements FakeMBean {

@Override
protected String name() {
return "FakeName";
}

@Override
public Section toProtobuf() {
return Section.newBuilder().build();
}

@Override
public void foo() {
// nothing to do
}
}
}

+ 1
- 1
server/sonar-webserver-core/build.gradle 파일 보기

@@ -28,7 +28,7 @@ dependencies {
compile 'org.apache.httpcomponents:httpclient'
compile 'org.apache.logging.log4j:log4j-api'
compile 'org.apache.tomcat.embed:tomcat-embed-core'
compile 'org.apache.commons:commons-dbcp2'
compile 'com.zaxxer:HikariCP'
compile 'org.slf4j:jul-to-slf4j'
compile 'org.slf4j:slf4j-api'
compile 'org.sonarsource.api.plugin:sonar-plugin-api'

+ 6
- 57
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/DbConnectionSection.java 파일 보기

@@ -19,9 +19,9 @@
*/
package org.sonar.server.platform.monitoring;

import org.apache.commons.dbcp2.BasicDataSource;
import org.sonar.api.SonarQubeSide;
import org.sonar.api.SonarRuntime;
import org.sonar.db.DatabaseMBean;
import org.sonar.db.DbClient;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Section;
import org.sonar.server.platform.db.migration.version.DatabaseVersion;
@@ -31,15 +31,14 @@ import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;
/**
* Information about database connection pool
*/
public class DbConnectionSection extends BaseSectionMBean implements DbConnectionSectionMBean {
public class DbConnectionSection extends DatabaseMBean implements DbConnectionSectionMBean {

private final DatabaseVersion dbVersion;
private final DbClient dbClient;
private final SonarRuntime runtime;

public DbConnectionSection(DatabaseVersion dbVersion, DbClient dbClient, SonarRuntime runtime) {
super(dbClient);
this.dbVersion = dbVersion;
this.dbClient = dbClient;
this.runtime = runtime;
}

@@ -53,51 +52,6 @@ public class DbConnectionSection extends BaseSectionMBean implements DbConnectio
return dbVersion.getStatus().name();
}

@Override
public int getPoolActiveConnections() {
return commonsDbcp().getNumActive();
}

@Override
public int getPoolMaxActiveConnections() {
return commonsDbcp().getMaxTotal();
}

@Override
public int getPoolIdleConnections() {
return commonsDbcp().getNumIdle();
}

@Override
public int getPoolMaxIdleConnections() {
return commonsDbcp().getMaxIdle();
}

@Override
public int getPoolMinIdleConnections() {
return commonsDbcp().getMinIdle();
}

@Override
public int getPoolInitialSize() {
return commonsDbcp().getInitialSize();
}

@Override
public long getPoolMaxWaitMillis() {
return commonsDbcp().getMaxWaitMillis();
}

@Override
public boolean getPoolRemoveAbandoned() {
return commonsDbcp().getRemoveAbandonedOnBorrow();
}

@Override
public int getPoolRemoveAbandonedTimeoutSeconds() {
return commonsDbcp().getRemoveAbandonedTimeout();
}

@Override
public Section toProtobuf() {
Section.Builder protobuf = Section.newBuilder();
@@ -108,18 +62,13 @@ public class DbConnectionSection extends BaseSectionMBean implements DbConnectio
}

private void completePoolAttributes(Section.Builder protobuf) {
setAttribute(protobuf, "Pool Total Connections", getPoolTotalConnections());
setAttribute(protobuf, "Pool Active Connections", getPoolActiveConnections());
setAttribute(protobuf, "Pool Max Connections", getPoolMaxActiveConnections());
setAttribute(protobuf, "Pool Initial Size", getPoolInitialSize());
setAttribute(protobuf, "Pool Idle Connections", getPoolIdleConnections());
setAttribute(protobuf, "Pool Max Connections", getPoolMaxConnections());
setAttribute(protobuf, "Pool Min Idle Connections", getPoolMinIdleConnections());
setAttribute(protobuf, "Pool Max Idle Connections", getPoolMaxIdleConnections());
setAttribute(protobuf, "Pool Max Wait (ms)", getPoolMaxWaitMillis());
setAttribute(protobuf, "Pool Remove Abandoned", getPoolRemoveAbandoned());
setAttribute(protobuf, "Pool Remove Abandoned Timeout (seconds)", getPoolRemoveAbandonedTimeoutSeconds());
setAttribute(protobuf, "Pool Max Lifetime (ms)", getPoolMaxLifeTimeMillis());
}

private BasicDataSource commonsDbcp() {
return (BasicDataSource) dbClient.getDatabase().getDataSource();
}
}

+ 11
- 17
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/DbConnectionSectionMBean.java 파일 보기

@@ -27,21 +27,24 @@ public interface DbConnectionSectionMBean {
String getMigrationStatus();

/**
*
* Get the number of currently active connections in the pool.
*/
int getPoolActiveConnections();

/**
* The maximum number of active connections that can be allocated from this pool at the same time, or negative for no limit.
* The maximum number of connections that can be allocated from this pool at the same time, or negative for no limit.
*/
int getPoolMaxActiveConnections();
int getPoolMaxConnections();

int getPoolIdleConnections();
/**
* Total number of connections currently in the pool
*/
int getPoolTotalConnections();

/**
* The maximum number of connections that can remain idle in the pool, without extra ones being released, or negative for no limit.
* Get the number of currently idle connections in the pool.
*/
int getPoolMaxIdleConnections();
int getPoolIdleConnections();

/**
* The minimum number of connections that can remain idle in the pool, without extra ones being created, or zero to create none.
@@ -49,9 +52,9 @@ public interface DbConnectionSectionMBean {
int getPoolMinIdleConnections();

/**
* The initial number of connections that are created when the pool is started.
* Maximum lifetime of a connection in the pool.
*/
int getPoolInitialSize();
long getPoolMaxLifeTimeMillis();

/**
* The maximum number of milliseconds that the pool will wait
@@ -59,13 +62,4 @@ public interface DbConnectionSectionMBean {
*/
long getPoolMaxWaitMillis();

/**
* Flag to remove abandoned connections if they exceed the {@link #getPoolRemoveAbandonedTimeoutSeconds()}.
*/
boolean getPoolRemoveAbandoned();

/**
* Timeout in seconds before an abandoned connection can be removed.
*/
int getPoolRemoveAbandonedTimeoutSeconds();
}

+ 1
- 0
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java 파일 보기

@@ -29,6 +29,7 @@ import org.sonar.api.platform.Server;
import org.sonar.api.security.SecurityRealm;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.process.systeminfo.BaseSectionMBean;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.authentication.IdentityProviderRepository;
import org.sonar.server.log.ServerLogging;

+ 1
- 1
server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/BaseSectionMBeanTest.java 파일 보기

@@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;

public class BaseSectionMBeanTest {

private FakeSection underTest = new FakeSection();
private final FakeSection underTest = new FakeSection();

@Test
public void test_registration() throws Exception {

+ 10
- 11
server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/DbConnectionSectionTest.java 파일 보기

@@ -38,9 +38,9 @@ public class DbConnectionSectionTest {
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);

private DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
private SonarRuntime runtime = mock(SonarRuntime.class);
private DbConnectionSection underTest = new DbConnectionSection(databaseVersion, dbTester.getDbClient(), runtime);
private final DatabaseVersion databaseVersion = mock(DatabaseVersion.class);
private final SonarRuntime runtime = mock(SonarRuntime.class);
private final DbConnectionSection underTest = new DbConnectionSection(databaseVersion, dbTester.getDbClient(), runtime);

@Test
public void jmx_name_is_not_empty() {
@@ -50,13 +50,12 @@ public class DbConnectionSectionTest {
@Test
public void pool_info() {
ProtobufSystemInfo.Section section = underTest.toProtobuf();
assertThat(attribute(section, "Pool Max Connections").getLongValue()).isGreaterThan(0L);
assertThat(attribute(section, "Pool Idle Connections").getLongValue()).isGreaterThanOrEqualTo(0L);
assertThat(attribute(section, "Pool Min Idle Connections").getLongValue()).isGreaterThanOrEqualTo(0L);
assertThat(attribute(section, "Pool Max Idle Connections").getLongValue()).isGreaterThanOrEqualTo(0L);
assertThat(attribute(section, "Pool Max Wait (ms)")).isNotNull();
assertThat(attribute(section, "Pool Remove Abandoned")).isNotNull();
assertThat(attribute(section, "Pool Remove Abandoned Timeout (seconds)").getLongValue()).isGreaterThanOrEqualTo(0L);
assertThat(attribute(section, "Pool Total Connections").getLongValue()).isNotNegative();
assertThat(attribute(section, "Pool Active Connections").getLongValue()).isNotNegative();
assertThat(attribute(section, "Pool Idle Connections").getLongValue()).isNotNegative();
assertThat(attribute(section, "Pool Max Connections").getLongValue()).isNotNegative();
assertThat(attribute(section, "Pool Min Idle Connections")).isNotNull();
assertThat(attribute(section, "Pool Max Lifetime (ms)")).isNotNull();
}

@Test
@@ -64,7 +63,7 @@ public class DbConnectionSectionTest {
when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.COMPUTE_ENGINE);
assertThat(underTest.toProtobuf().getName()).isEqualTo("Compute Engine Database Connection");

when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.SERVER );
when(runtime.getSonarQubeSide()).thenReturn(SonarQubeSide.SERVER);
assertThat(underTest.toProtobuf().getName()).isEqualTo("Web Database Connection");
}
}

+ 1
- 0
server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/FakeSection.java 파일 보기

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

import org.sonar.process.systeminfo.BaseSectionMBean;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

public class FakeSection extends BaseSectionMBean implements FakeSectionMBean {

+ 3
- 6
sonar-application/src/main/assembly/conf/sonar.properties 파일 보기

@@ -64,15 +64,12 @@

# The minimum number of connections that can remain idle in the pool,
# without extra ones being created, or zero to create none.
#sonar.jdbc.minIdle=2
#sonar.jdbc.minIdle=10

# The maximum number of milliseconds that the pool will wait (when there
# are no available connections) for a connection to be returned before
# throwing an exception, or <= 0 to wait indefinitely.
#sonar.jdbc.maxWait=5000

#sonar.jdbc.minEvictableIdleTimeMillis=600000
#sonar.jdbc.timeBetweenEvictionRunsMillis=30000
#sonar.jdbc.maxWait=8000



@@ -121,7 +118,7 @@
# The default value is 25.
#sonar.web.http.acceptCount=25

# The number of milliseconds this Connector will wait for another HTTP request before closing the
# The number of milliseconds this Connector will wait for another HTTP request before closing the
# connection. The default value is to use the value that has been set for the connectionTimeout
# attribute. Use a value of -1 to indicate no (i.e. infinite) timeout.
# The default value is 60000 (ms).

Loading…
취소
저장