How to change Play 2 Framework DB configuration at

2019-05-07 17:53发布

We are using Play 2.1.1 and its built-in JPA integration (JPA.em() etc).

  • How can we dynamically change the db.pass property? Play.application().configuration() seems to be immutable as of Play 2.1. (or we're at least not aware of the mutators)
  • If we are able to change db.pass, how can we reload the DB configuration so that JPA.em() returns an EntityManager using the new password?

What we are trying to avoid is having to recreate the EntityManager using EntityManagerFactory. We want to continue to let Play manage that in the JPA helper class.

Background

The system has a default DB configuration for running locally. When deployed to a server, the DB password is dynamically set on the running application using the following script:

#!/bin/bash 
stty -echo 
read -p "Password: " PASS 
stty echo 
curl -k https://127.0.0.1:8443/someUrl/pwd --data "password=$PASS" 

The application receives this data and then recreates the Hibernate SessionFactory. Our new Play app will be required to do something similar.

2条回答
该账号已被封号
2楼-- · 2019-05-07 18:35

To answer my own question, at first we solved the problem of updating the immutable configuration at runtime by overriding Configuration.onLoadConfig with the following:

  1. If configuration indicates that production.level is PROD
  2. Read the password from stdin
  3. Create a new configuration by converting the old one to a map and building a new one with ConfigFactory.parseMap, with the new parameter as well
  4. Return super.onLoadConfig

However, this still didn't address that the problem of reloading the DB configuration. In the end, my colleague created a Play! plugin which essentially a copy of some JPA classes with the added capability of being reloaded with a Map of configuration properties.

Update

The "hook" is the additional static method which the plugin adds to the JPA class (e.g. reloadWithProperties). This method creates a new data source which is then rebound in JNDI.

查看更多
地球回转人心会变
3楼-- · 2019-05-07 18:43

The key is to use the ConfigFactory to create a new Config entry. This new Config contains an entry for password with the value coming from your http call to your password service.

A new Configuration is created using the new Config, which in turn falls back to the original Config from the original Configuration.

Basically the new password entry supersedes the original.

It sound long winded when you say it, but the code is pretty readable.

public class Global extends GlobalSettings {

    // inject http client to make call for password

    @Override
    public Configuration onLoadConfig(Configuration configuration, File file, ClassLoader classLoader) {
        final Config config = ConfigFactory.parseString(String.format("db.default.user=%s", callPasswordService()));

        return new Configuration(config.withFallback(configuration.getWrappedConfiguration().underlying()));
    }
}
查看更多
登录 后发表回答