0

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.

Pitchu
  • 27
  • 4

1 Answers1

0

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.

Semyon Kirekov
  • 1,237
  • 8
  • 20