Dead simple configuration
Whole frameworks have been written with the purpose of handling the configuration of your application. I prefer a simpler way.
If by configuration we mean “everything that is likely to vary between deploys”, it follows that we should try and keep configuration simple. In Java, the simplest option is the humble properties file. The downside of a properties file is that you have to restart your application when you want it to pick up changes. Or do you?
Here’s a simple method I’ve used on several projects:
public class MyAppConfig extends AppConfiguration {
private static MyAppConfig instance = new MyAppConfig();
public static MyAppConfig instance() {
return instance;
}
private MyAppConfig() {
this("myapp.properties");
}
public String getServiceUrl() {
return getRequiredProperty("service.url");
}
public boolean getShouldStartSlow() {
return getFlag("start-slow", false);
}
public int getHttpPort(int defaultPort) {
return getIntProperty("myapp.http.port", defaultPort);
}
}
The AppConfiguration class looks like this:
public abstract class AppConfiguration {
private static Logger log = LoggerFactory.getLogger(AppConfiguration.class);
private long nextCheckTime = 0;
private long lastLoadTime = 0;
private Properties properties = new Properties();
private final File configFile;
protected AppConfiguration(String filename) {
this.configFile = new File(filename);
}
public String getProperty(String propertyName, String defaultValue) {
String result = getProperty(propertyName);
if (result == null) {
log.trace("Missing property {} in {}", propertyName, properties.keySet());
return defaultValue;
}
return result;
}
public String getRequiredProperty(String propertyName) {
String result = getProperty(propertyName);
if (result == null) {
throw new RuntimeException("Missing property " + propertyName);
}
return result;
}
private String getProperty(String propertyName) {
if (System.getProperty(propertyName) != null) {
log.trace("Reading {} from system properties", propertyName);
return System.getProperty(propertyName);
}
if (System.getenv(propertyName.replace('.', '\_')) != null) {
log.trace("Reading {} from environment", propertyName);
return System.getenv(propertyName.replace('.', '\_'));
}
ensureConfigurationIsFresh();
return properties.getProperty(propertyName);
}
private synchronized void ensureConfigurationIsFresh() {
if (System.currentTimeMillis() < nextCheckTime) return;
nextCheckTime = System.currentTimeMillis() + 10000;
log.trace("Rechecking {}", configFile);
if (!configFile.exists()) {
log.error("Missing configuration file {}", configFile);
}
if (lastLoadTime >= configFile.lastModified()) return;
lastLoadTime = configFile.lastModified();
log.debug("Reloading {}", configFile);
try (FileInputStream inputStream = new FileInputStream(configFile)) {
properties.clear();
properties.load(inputStream);
} catch (IOException e) {
throw new RuntimeException("Failed to load " + configFile, e);
}
}
}
This reads the configuration file in an efficient way and updates the settings as needed. It supports environment variables and system properties as defaults. And it even gives a pretty good log of what’s going on.
For the full source code and a magic DataSource which updates automatically, see this gist: https://gist.github.com/jhannes/b8b143e0e5b287d73038
Enjoy!