Skip to content

Instantly share code, notes, and snippets.

@wendelicious
Created June 11, 2014 05:40
Show Gist options
  • Save wendelicious/3015d304a2d04a5f8941 to your computer and use it in GitHub Desktop.
Save wendelicious/3015d304a2d04a5f8941 to your computer and use it in GitHub Desktop.
package com.dietsodasoftware.configuration.archaius;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.PollResult;
import com.netflix.config.PolledConfigurationSource;
import com.netflix.config.sources.URLConfigurationSource;
import com.netflix.config.util.ConfigurationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
/**
* A polled configuration source based on a set of config names. For each poll,
* it always returns the complete union of properties defined in all files. If one property
* is defined in more than one config file, the value in file later on the list will override
* the value in the previous one. The content of the config file should conform to the properties file format.
*
* @author wendel.schultz
*
*/
public class NamedConfigurationSource implements PolledConfigurationSource {
private final String[] configNames;
/**
* System property name to define a set of config names to be used by the
* default constructor.
*/
public static final String CONFIG_NAMES = "dietsoda.configurationSource.additionalNames";
/**
* Default configuration file name to be used by default constructor. This file should
* be on the classpath. The file name can be overridden by the value of system property
* <code>archaius.configurationSource.defaultFileName</code>
*/
public static final String DEFAULT_CONFIG_FILE_FROM_CLASSPATH = URLConfigurationSource.DEFAULT_CONFIG_FILE_FROM_CLASSPATH;
private static final Logger logger = LoggerFactory.getLogger(NamedConfigurationSource.class);
/**
* Create an instance with a list of names to be used.
*
* @param names list of names to be used
*/
public NamedConfigurationSource(String... names) {
configNames = names;
}
/**
* Create the instance for the default list of names, which is composed by the following order
*
* <ul>
* <li>A configuration file (default name to be <code>config.properties</code>, see {@link #DEFAULT_CONFIG_FILE_FROM_CLASSPATH}) on the classpath
* <li>A list of names defined by system property {@value #CONFIG_NAMES} with values separated by comma <code>","</code>.
* </ul>
*/
public NamedConfigurationSource() {
List<String> nameList = new ArrayList<>();
String configFromClasspath = getConfigFileNameFromClasspath();
if (configFromClasspath != null) {
nameList.add(configFromClasspath);
}
String[] fileNames = getDefaultFileSources();
if (fileNames.length != 0) {
nameList.addAll(Arrays.asList(fileNames));
}
if (nameList.size() == 0) {
configNames = new String[0];
logger.warn("No configs will be polled as dynamic configuration sources.");
logger.info("To enable configs as dynamic configuration sources, define System property "
+ CONFIG_NAMES + " or make " + DEFAULT_CONFIG_FILE_FROM_CLASSPATH + " available on classpath.");
} else {
configNames = nameList.toArray(new String[nameList.size()]);
logger.info("Config names to be used as dynamic configuration source: " + nameList);
}
}
private String getConfigFileNameFromClasspath() {
URL url = null;
// attempt to load from the context classpath
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader != null) {
url = loader.getResource(DEFAULT_CONFIG_FILE_FROM_CLASSPATH);
}
if (url == null) {
// attempt to load from the system classpath
url = ClassLoader.getSystemResource(DEFAULT_CONFIG_FILE_FROM_CLASSPATH);
}
if (url == null) {
// attempt to load from the system classpath
url = URLConfigurationSource.class.getResource(DEFAULT_CONFIG_FILE_FROM_CLASSPATH);
}
if(url == null){
return null;
}
return DEFAULT_CONFIG_FILE_FROM_CLASSPATH.replace(".properties", "");
}
public List<String> getConfigNames() {
return Collections.unmodifiableList(Arrays.asList(configNames));
}
private static final String[] getDefaultFileSources() {
String name = System.getProperty(CONFIG_NAMES);
String[] fileNames;
if (name != null) {
fileNames = name.split(",");
for(int i = 0; i < fileNames.length; i++){
fileNames[i] = fileNames[i].replace(".properties", "");
}
} else {
fileNames = new String[0];
}
return fileNames;
}
/**
* Retrieve the content of the property files. For each poll, it always
* returns the complete union of properties defined in all configs. If one
* property is defined in content of more than one config, the value in file later on the
* list will override the value in the previous one.
*
* @param initial this parameter is ignored by the implementation
* @param checkPoint this parameter is ignored by the implementation
* @throws java.io.IOException IOException occurred in file operation
*/
@Override
public PollResult poll(boolean initial, Object checkPoint)
throws IOException {
if (configNames == null || configNames.length == 0) {
return PollResult.createFull(null);
}
ConfigurationManager.getConfigInstance();
Map<String, Object> map = new HashMap<String, Object>();
for (String configName: configNames) {
String defaultConfigFileName = configName + ".properties";
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(defaultConfigFileName);
if (url == null) {
throw new IOException("Cannot locate " + defaultConfigFileName + " as a classpath resource.");
}
Properties props = ConfigurationUtils.getPropertiesFromFile(url, new HashSet<String>(), ConfigurationManager.PROP_NEXT_LOAD);
String environment = ConfigurationManager.getDeploymentContext().getDeploymentEnvironment();
if (environment != null && environment.length() > 0) {
String envConfigFileName = configName + "-" + environment + ".properties";
url = loader.getResource(envConfigFileName);
if (url != null) {
Properties envProps = ConfigurationManager.getPropertiesFromFile(url);
if (envProps != null) {
props.putAll(envProps);
}
}
}
for (Entry<Object, Object> entry: props.entrySet()) {
map.put((String) entry.getKey(), entry.getValue());
}
}
return PollResult.createFull(map);
}
@Override
public String toString() {
return "NamedConfigurationSource [configNames=" + Arrays.toString(configNames)
+ "]";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment