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.
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()));
}
}
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:
- If configuration indicates that production.level is PROD
- Read the password from stdin
- 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
- 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.