I have a Spring application which connects with a database using ComboPooledDataSource, LocalContainerEntityManagerFactoryBean and JpaTransactionManager. The application works fine. Following is my configuration.
<!-- DataSource JavaConfig -->
@Configuration
public class DataSourceConfig
{
@Bean
public DataSource dataSource()
{
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(getUsername());
dataSource.setPassword(getPassword());
dataSource.setDriverClass(driverClassName);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setMinPoolSize(minPoolSize);
dataSource.setMaxPoolSize(maxPoolSize);
dataSource.setCheckoutTimeout(checkoutTimeout);
dataSource.setMaxIdleTime(maxIdleTime);
dataSource.setIdleConnectionTestPeriod(idleConnectionTestPeriod);
dataSource.setAcquireRetryAttempts(acquireRetryAttempts);
dataSource.setAcquireRetryDelay(acquireRetryDelay);
return dataSource;
} catch (Exception ex) {
LOGGER.error("Error occurred while initializing datasource ", ex);
}
return null;
}
}
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Entity manager factory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" depends-on="dbMigrationService">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="${hibernate.showSql}"/>
<property name="databasePlatform" value="${hibernate.dialect}"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<!-- batch writing -->
<prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop>
<prop key="hibernate.order_inserts">${hibernate.order_inserts}</prop>
<prop key="hibernate.order_updates">${hibernate.order_updates}</prop>
<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
</props>
</property>
</bean>
But my database password is being rotated frequently using Hashicorp vault and I've the new password. Now i need to use the new password to make connections with the database, also i need to do this without restarting the application. So is it possible to change the database credentials used in the datasource while application is running? If yes What should i need to do for this? Can someone help me with this? Thanks.
I think that you need to do something like that.
public class DataSourceStateListener {
@Autowired
private DataSource dataSource;
@EventListener
public void changeDataSourceCredentials(DBCredentialsEvent event) {
dataSource.setPassword(event.getPassword());
}
}
DBCredentialsEvent
should be fired when new credentials has been requested from Vault.
@Autowired
private ApplicationEventPublisher eventPublisher;
eventPublisher(new DBCredentialsEvent(vaultPassword));
I found these lines of code in AbstractComboPooledDataSource
.
public void setUser( String user ) {
if ( diff( dmds.getUser(), user ) ) {
dmds.setUser( user );
this.resetPoolManager( false );
}
}
public void setPassword( String password ) {
if ( diff( dmds.getPassword(), password ) ) {
dmds.setPassword( password );
this.resetPoolManager( false );
}
}
So, it seems that changing either database user or password does reset the pool manager as well. Anyway, the behaviour should be tested before going to production.