springhibernatedatasourcec3p0hibernate-entitymanager

How to use the new database credentials to connect with database without restarting the application


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.


Solution

  • 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.