Java Dynamic Property Configuration

Link to GitHub repo: https://github.com/benkauffman/java-gradle-jetty-webservice

While developing a Java API for one of our web applications, the problem of storing and retrieving settings turned from what seemed a simple task into a frustrating disaster. Having come from the .NET world, I thought it made sense to use an XML file to retrieve the settings based on XML elements. Using the java.util.Properties I was able to load an XML document into file stream and then retrieve the element string value by name.

Example with an XML file


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Settings {

private static final Logger LOGGER = LoggerFactory.getLogger(Settings.class);

public static int getIntSetting(String settingName) throws IOException {
return Integer.parseInt(getStringSetting(settingName));
}

public static boolean getBooleanSetting(String settingName) throws IOException {
return Boolean.parseBoolean(getStringSetting(settingName));
}

public static String[] getArraySetting(String settingName) throws IOException {
return getStringSetting(settingName).split(",");
}

public static String getStringSetting(String name) {

Properties prop = new Properties();

String fileNm = "settings.xml";
LOGGER.debug("Getting setting " + name + " from " + fileNm);

//load a properties file from classpath
InputStream inputStream =
Settings.class.getClassLoader().getResourceAsStream(fileNm);

try {
//load the properties from the input stream
prop.loadFromXML(inputStream);
} catch (IOException e) {
LOGGER.error("Error when attempting to get " + name + " from " + fileNm);
}

//get the property value and return it
return prop.getProperty(name);

}

}

java03

Those of you with Java experience probably already know that using XMLs, although supported, is a little unconventional. I later found that the “.properties” file extension is the Java standard for storing properties. It was a simple revision to correct this; I just needed to change the load method for the Properties class.

Example with a “.properties” file

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Settings {

private static final Logger LOGGER = LoggerFactory.getLogger(Settings.class);

public static int getIntSetting(String settingName) throws IOException {
return Integer.parseInt(getStringSetting(settingName));
}

public static boolean getBooleanSetting(String settingName) throws IOException {
return Boolean.parseBoolean(getStringSetting(settingName));
}

public static String[] getArraySetting(String settingName) throws IOException {
return getStringSetting(settingName).split(",");
}

public static String getStringSetting(String name) {

Properties prop = new Properties();

String fileNm = "settings.properties";
LOGGER.debug("Getting setting " + name + " from " + fileNm);

//load a properties file from classpath
InputStream inputStream =
Settings.class.getClassLoader().getResourceAsStream(fileNm);

try {
//load the properties from the input stream
prop.loadFromXML(inputStream);
} catch (IOException e) {
LOGGER.error("Error when attempting to get " + name + " from " + fileNm);
}

//get the property value and return it
return prop.getProperty(name);

}

}

java02

While this might seem to be a valid method, you can see that the properties file is being loaded and read each time a setting is returned. This means that when the settings for the database connection string is returned, the application loads and reads the properties file 4 separate times (host, username, password & port). Though this might be OK if the program was only going to read the settings at application startup, I needed it to read them in several methods at various times throughout the application. You can imagine the performance deficit that this may have caused if I didn’t address the design.

Furthermore, I found that several different configurations were needed across each platform; rather than setting up each platform independently, I wanted a simple and efficient design to load properties based on the environment that the application was running in. While trying to resolve this with a manual approach, I ran across the Netflix Archaius configuration manager: http://techblog.netflix.com/2012/06/annoucing-archaius-dynamic-properties.html. Apparently Netflix had encountered a similar problem, and created a library which can be used to dynamically get properties based on the environment and other predefined values. This design allowed me to create multiple configuration files, name each of them uniquely and then specify which of the files were loaded at the initial application runtime.

I named each properties file to match the environment server name, which ensured that the correct properties file was loaded once the API was launched. For example “test-server.properties” and “prod-server.properties” would match the name of the “test-server” and “prod-server” host and the associated properties would be loaded. The configuration manager only reads the properties file once. After reading it, it stores the values in memory, which allows for the most efficient retrieval.

You can see in the final example that the properties are loaded based on the host/computer name. If the host name isn’t available, the default properties file is loaded. If a properties file doesn’t exist for the hostname, then the application will error with a java.io.IOException.

Example with Netflix Archaius Dynamic Configuration Manager

import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Map;

public class Settings {
private static final Logger LOGGER = LoggerFactory.getLogger(Settings.class);
private static DynamicPropertyFactory dynamicPropertyFactory;

static {
String hostname;
Map<String, String> env = System.getenv();

if (env.containsKey("COMPUTERNAME"))
hostname = env.get("COMPUTERNAME");
else if (env.containsKey("HOSTNAME"))
hostname = env.get("HOSTNAME");
else
hostname = "default";

try {
ConfigurationManager.loadCascadedPropertiesFromResources(hostname);
} catch (IOException e) {
LOGGER.error("Unable to load properties file ", e);
}

LOGGER.debug("Settings hostname = " + hostname);
dynamicPropertyFactory = DynamicPropertyFactory.getInstance();
}

public static String getStringSetting(String settingName){
return dynamicPropertyFactory.getStringProperty(settingName, null).get();
}

public static int getIntSetting(String settingName){
return dynamicPropertyFactory.getIntProperty(settingName, -1).get();
}

public static boolean getBooleanSetting(String settingName){
return dynamicPropertyFactory.getBooleanProperty(settingName, false).get();
}

public static String[] getArraySetting(String settingName){
return getStringSetting(settingName).split(",");
}

}

java01

In conclusion, if you’ve ever needed dynamic configurations for multiple environments, Netflix Archaius is the way to go. With very simple logic, you can create a multifaceted settings method that will return various property types with ease. If the dynamic design of this library doesn’t intrigue you; think of the performance gains achieved by injecting your configuration properties at runtime rather than performing multiple IO processes during execution.

1