Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AG-237: Ability to change DataSource settings after starting #102

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
114 changes: 97 additions & 17 deletions agroal-pool/src/main/java/io/agroal/pool/DataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,25 @@
import io.agroal.api.AgroalDataSourceListener;
import io.agroal.api.AgroalDataSourceMetrics;
import io.agroal.api.AgroalPoolInterceptor;
import io.agroal.api.configuration.AgroalConnectionFactoryConfiguration;
import io.agroal.api.configuration.AgroalDataSourceConfiguration;
import io.agroal.api.configuration.AgroalDataSourceConfiguration.DataSourceImplementation;
import io.agroal.api.configuration.supplier.AgroalConnectionFactoryConfigurationSupplier;
import io.agroal.api.configuration.supplier.AgroalConnectionPoolConfigurationSupplier;
import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier;
import io.agroal.api.security.NamePrincipal;
import io.agroal.api.security.SimplePassword;
import io.agroal.pool.util.ReloadDataSourceUtil;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;

import static java.util.Collections.emptyList;
Expand All @@ -26,64 +37,67 @@ public final class DataSource implements AgroalDataSource {

private static final long serialVersionUID = 6485903416474487024L;

private final AgroalDataSourceConfiguration configuration;
private final Pool connectionPool;
private final AtomicReference<AgroalDataSourceConfiguration> configuration = new AtomicReference<>();
private final AtomicReference<Pool> connectionPool = new AtomicReference<>();
private final AtomicReference<AgroalDataSourceListener[]> listeners = new AtomicReference<>();

public DataSource(AgroalDataSourceConfiguration dataSourceConfiguration, AgroalDataSourceListener... listeners) {
configuration = dataSourceConfiguration;
if ( configuration.dataSourceImplementation() == AgroalDataSourceConfiguration.DataSourceImplementation.AGROAL_POOLLESS ) {
connectionPool = new Poolless( dataSourceConfiguration.connectionPoolConfiguration(), listeners );
configuration.set(dataSourceConfiguration);
if ( configuration.get().dataSourceImplementation() == AgroalDataSourceConfiguration.DataSourceImplementation.AGROAL_POOLLESS ) {
connectionPool.set(new Poolless( dataSourceConfiguration.connectionPoolConfiguration(), listeners ));
} else {
connectionPool = new ConnectionPool( dataSourceConfiguration.connectionPoolConfiguration(), listeners );
connectionPool.set(new ConnectionPool( dataSourceConfiguration.connectionPoolConfiguration(), listeners ));
}

dataSourceConfiguration.registerMetricsEnabledListener( connectionPool );
connectionPool.onMetricsEnabled( dataSourceConfiguration.metricsEnabled() );
connectionPool.init();
this.listeners.set(listeners);

dataSourceConfiguration.registerMetricsEnabledListener( connectionPool.get());
connectionPool.get().onMetricsEnabled( dataSourceConfiguration.metricsEnabled() );
connectionPool.get().init();
}

// --- AgroalDataSource methods //

@Override
public void setPoolInterceptors(Collection<? extends AgroalPoolInterceptor> interceptors) {
connectionPool.setPoolInterceptors( interceptors == null ? emptyList() : interceptors );
connectionPool.get().setPoolInterceptors( interceptors == null ? emptyList() : interceptors );
}

@Override
public List<AgroalPoolInterceptor> getPoolInterceptors() {
return connectionPool.getPoolInterceptors();
return connectionPool.get().getPoolInterceptors();
}

@Override
public AgroalDataSourceConfiguration getConfiguration() {
return configuration;
return configuration.get();
}

@Override
public AgroalDataSourceMetrics getMetrics() {
return connectionPool.getMetrics();
return connectionPool.get().getMetrics();
}

@Override
public void flush(FlushMode mode) {
connectionPool.flushPool( mode );
connectionPool.get().flushPool( mode );
}

@Override
public boolean isHealthy(boolean newConnection) throws SQLException {
return connectionPool.isHealthy( newConnection );
return connectionPool.get().isHealthy( newConnection );
}

@Override
public void close() {
connectionPool.close();
connectionPool.get().close();
}

// --- DataSource methods //

@Override
public Connection getConnection() throws SQLException {
return connectionPool.getConnection();
return connectionPool.get().getConnection();
}

@Override
Expand Down Expand Up @@ -129,4 +143,70 @@ public void setLoginTimeout(int seconds) throws SQLException {
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException( "Not Supported" );
}

// --- DataSource method //
/**
* Reload current DataSource
* @param properties - key-value structure. Available keys: ReloadDataSourceUtil.AGROAL_JDBC_URL, ReloadDataSourceUtil.AGROAL_USERNAME, ReloadDataSourceUtil.AGROAL_PASSWORD
* @param flushMode - flush mode
*/
public synchronized void reloadDataSourceWithNewCredentials(Properties properties, FlushMode flushMode) {
flush(flushMode);
this.connectionPool.get().close();

AgroalConnectionPoolConfigurationSupplier agroalConnectionPoolConfigurationSupplier = new AgroalConnectionPoolConfigurationSupplier(getConfiguration().connectionPoolConfiguration());
AgroalConnectionFactoryConfigurationSupplier agroalConnFactConfSupplier = new AgroalConnectionFactoryConfigurationSupplier();

AgroalConnectionFactoryConfiguration configuration = getConfiguration().connectionPoolConfiguration().connectionFactoryConfiguration();
agroalConnFactConfSupplier.autoCommit(configuration.autoCommit());
agroalConnFactConfSupplier.trackJdbcResources(configuration.trackJdbcResources());
agroalConnFactConfSupplier.loginTimeout(configuration.loginTimeout());
if (properties.containsKey(ReloadDataSourceUtil.AGROAL_JDBC_URL)) {
agroalConnFactConfSupplier.jdbcUrl(properties.getProperty(ReloadDataSourceUtil.AGROAL_JDBC_URL));
} else {
agroalConnFactConfSupplier.jdbcUrl(configuration.jdbcUrl());
}
agroalConnFactConfSupplier.initialSql(configuration.initialSql());
agroalConnFactConfSupplier.connectionProviderClass(configuration.connectionProviderClass());
agroalConnFactConfSupplier.jdbcTransactionIsolation(AgroalConnectionFactoryConfiguration.TransactionIsolation.fromLevel(configuration.jdbcTransactionIsolation().level()));

if (properties.containsKey(ReloadDataSourceUtil.AGROAL_USERNAME)) {
NamePrincipal namePrincipal = new NamePrincipal(properties.getProperty(ReloadDataSourceUtil.AGROAL_USERNAME));

agroalConnFactConfSupplier.principal(namePrincipal);
agroalConnFactConfSupplier.recoveryPrincipal(namePrincipal);
} else {
agroalConnFactConfSupplier.principal(configuration.principal());
agroalConnFactConfSupplier.recoveryPrincipal(configuration.recoveryPrincipal());
}

if (properties.containsKey(ReloadDataSourceUtil.AGROAL_PASSWORD)) {
SimplePassword simplePassword = new SimplePassword(properties.getProperty(ReloadDataSourceUtil.AGROAL_PASSWORD));

agroalConnFactConfSupplier.credential(simplePassword);
agroalConnFactConfSupplier.recoveryCredential(simplePassword);
} else {
agroalConnFactConfSupplier.credential(configuration.credentials());
agroalConnFactConfSupplier.recoveryCredential(configuration.recoveryCredentials());
}

configuration.jdbcProperties().forEach((k, v) -> agroalConnFactConfSupplier.jdbcProperty((String)k, (String)v));

agroalConnectionPoolConfigurationSupplier.connectionFactoryConfiguration(agroalConnFactConfSupplier);

AgroalDataSourceConfigurationSupplier agroalDataSourceConfigurationSupplier = new AgroalDataSourceConfigurationSupplier();
agroalDataSourceConfigurationSupplier.connectionPoolConfiguration(agroalConnectionPoolConfigurationSupplier);
AgroalDataSourceConfiguration dataSourceConfiguration = agroalDataSourceConfigurationSupplier.get();

this.configuration.set(dataSourceConfiguration);
if (this.configuration.get().dataSourceImplementation() == DataSourceImplementation.AGROAL_POOLLESS) {
this.connectionPool.set(new Poolless(dataSourceConfiguration.connectionPoolConfiguration(), listeners.get()));
} else {
this.connectionPool.set(new ConnectionPool(dataSourceConfiguration.connectionPoolConfiguration(), listeners.get()));
}

dataSourceConfiguration.registerMetricsEnabledListener(this.connectionPool.get());
this.connectionPool.get().onMetricsEnabled(dataSourceConfiguration.metricsEnabled());
this.connectionPool.get().init();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.agroal.pool.util;

import io.agroal.api.AgroalDataSource;
import io.agroal.api.configuration.supplier.AgroalConnectionFactoryConfigurationSupplier;
import io.agroal.api.configuration.supplier.AgroalConnectionPoolConfigurationSupplier;
import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier;
import io.agroal.api.security.NamePrincipal;
import io.agroal.api.security.SimplePassword;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

public class ReloadDataSourceUtil {
public static final String AGROAL_JDBC_URL = "agroal_jdbc_url";
public static final String AGROAL_USERNAME = "agroal_principal";
public static final String AGROAL_PASSWORD = "agroal_credential";

public static boolean checkConnectionWithNewCredentials(String url, String username, String password) {
AgroalDataSourceConfigurationSupplier dataSourceConfiguration = new AgroalDataSourceConfigurationSupplier();
AgroalConnectionPoolConfigurationSupplier poolConfiguration = dataSourceConfiguration.connectionPoolConfiguration();
AgroalConnectionFactoryConfigurationSupplier connectionFactoryConfiguration = poolConfiguration.connectionFactoryConfiguration();

poolConfiguration
.initialSize(1)
.maxSize(3)
.minSize(1)
.maxLifetime(Duration.of(5, ChronoUnit.MINUTES))
.acquisitionTimeout(Duration.of(30, ChronoUnit.SECONDS));

connectionFactoryConfiguration
.jdbcUrl(url)
.principal(new NamePrincipal(username))
.credential(new SimplePassword(password));

AgroalDataSource datasource = null;
try {
datasource = AgroalDataSource.from(dataSourceConfiguration.get());
if (datasource.isHealthy(true)) {
return true;
}
datasource.flush(AgroalDataSource.FlushMode.ALL);
return false;
} catch (Exception ex) {
return false;
} finally {
if (datasource != null) {
datasource.flush(AgroalDataSource.FlushMode.ALL);
datasource.close();
}
}
}
}