package com.netflix.config.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.CombinedConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.config.ConcurrentCompositeConfiguration;
import com.netflix.config.ConcurrentMapConfiguration;
import com.netflix.config.ConfigurationManager;
public class ConfigurationUtils {
private static final Logger logger = LoggerFactory.getLogger(ConfigurationUtils.class);
public static ConcurrentCompositeConfiguration convertToConcurrentCompositeConfiguration(CombinedConfiguration config) {
ConcurrentCompositeConfiguration root = new ConcurrentCompositeConfiguration();
IdentityHashMap<Configuration, String> reverseMap = new IdentityHashMap<Configuration, String>();
for (String name: (Set<String>) config.getConfigurationNames()) {
Configuration child = config.getConfiguration(name);
reverseMap.put(child, name);
}
for (int i = 0; i < config.getNumberOfConfigurations(); i++) {
Configuration child = config.getConfiguration(i);
String name = reverseMap.get(child);
if (child instanceof CombinedConfiguration) {
CombinedConfiguration combinedConf = (CombinedConfiguration) child;
ConcurrentCompositeConfiguration newConf = convertToConcurrentCompositeConfiguration(combinedConf);
root.addConfiguration(newConf, name);
} else {
Configuration conf = new ConcurrentMapConfiguration(child);
root.addConfiguration((AbstractConfiguration) conf, name);
}
}
return root;
}
public static Map<String, Configuration> getAllNamedConfiguration(Configuration conf) {
List<Configuration> toProcess = new ArrayList<Configuration>();
Map<String, Configuration> map = new HashMap<String, Configuration>();
toProcess.add(conf);
while (!toProcess.isEmpty()) {
Configuration current = toProcess.remove(0);
if (current instanceof ConcurrentCompositeConfiguration) {
ConcurrentCompositeConfiguration composite = (ConcurrentCompositeConfiguration) current;
for (String name: composite.getConfigurationNames()) {
map.put(name, composite.getConfiguration(name));
}
for (Configuration c: composite.getConfigurations()) {
toProcess.add(c);
}
} else if (current instanceof CombinedConfiguration) {
CombinedConfiguration combined = (CombinedConfiguration) current;
for (String name: (Set<String>) combined.getConfigurationNames()) {
map.put(name, combined.getConfiguration(name));
}
for (int i = 0; i < combined.getNumberOfConfigurations(); i++) {
toProcess.add(combined.getConfiguration(i));
}
}
}
return map;
}
public static Properties getProperties(Configuration config) {
Properties p = new Properties();
if (config != null){
Iterator<String> it = config.getKeys();
while (it.hasNext()){
String key = it.next();
if (key != null) {
Object value = config.getProperty(key);
if (value != null) {
p.put(key, value);
} }
}
}
return p;
}
public static void loadProperties(Properties props, Configuration config) {
for (Entry<Object, Object> entry: props.entrySet()) {
config.setProperty((String) entry.getKey(), entry.getValue());
}
}
static void loadFromPropertiesFile(AbstractConfiguration config, String baseUrl, Set<String> loaded, String... nextLoadKeys) {
String nextLoad = getNextLoad(config, nextLoadKeys);
if (nextLoad == null) {
return;
}
String[] filesToLoad = nextLoad.split(",");
for (String fileName: filesToLoad) {
fileName = fileName.trim();
try {
URL url = new URL(baseUrl + "/" + fileName);
if (loaded.contains(url.toExternalForm())) {
logger.warn(url + " is already loaded");
continue;
}
loaded.add(url.toExternalForm());
PropertiesConfiguration nextConfig = new OverridingPropertiesConfiguration(url);
copyProperties(nextConfig, config);
logger.info("Loaded properties file " + url);
loadFromPropertiesFile(config, baseUrl, loaded, nextLoadKeys);
} catch (Throwable e) {
logger.warn("Unable to load properties file", e);
}
}
}
public static AbstractConfiguration getConfigFromPropertiesFile(URL startingUrl, Set<String> loaded, String... nextLoadKeys)
throws FileNotFoundException {
if (loaded.contains(startingUrl.toExternalForm())) {
logger.warn(startingUrl + " is already loaded");
return null;
}
PropertiesConfiguration propConfig = null;
try {
propConfig = new OverridingPropertiesConfiguration(startingUrl);
logger.info("Loaded properties file " + startingUrl);
} catch (ConfigurationException e) {
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException) {
throw (FileNotFoundException) cause;
} else {
throw new RuntimeException(e);
}
}
if (nextLoadKeys == null) {
return propConfig;
}
String urlString = startingUrl.toExternalForm();
String base = urlString.substring(0, urlString.lastIndexOf("/"));
loaded.add(startingUrl.toString());
loadFromPropertiesFile(propConfig, base, loaded, nextLoadKeys);
return propConfig;
}
public static void copyProperties(Configuration from, Configuration to) {
for (Iterator<String> i = from.getKeys(); i.hasNext(); ) {
String key = i.next();
if (key != null) {
Object value = from.getProperty(key);
if (value != null) {
to.setProperty(key, value);
}
}
}
}
public static Properties getPropertiesFromFile(URL startingUrl, Set<String> loaded, String... nextLoadKeys)
throws FileNotFoundException {
AbstractConfiguration config = getConfigFromPropertiesFile(startingUrl, loaded, nextLoadKeys);
return getProperties(config);
}
private static String getNextLoad(Configuration propConfig, String... nextLoadPropertyKeys) {
String nextLoadKeyToUse = null;
for (String key: nextLoadPropertyKeys) {
if (propConfig.getProperty(key) != null) {
nextLoadKeyToUse = key;
break;
}
}
if (nextLoadKeyToUse == null) {
return null;
}
ConcurrentMapConfiguration config = new ConcurrentMapConfiguration();
copyProperties(ConfigurationManager.getConfigInstance(), config);
copyProperties(propConfig, config);
List<Object> list = config.getList(nextLoadKeyToUse);
StringBuilder sb = new StringBuilder();
for (Object value: list) {
sb.append(value).append(",");
}
String nextLoad = sb.toString();
propConfig.clearProperty(nextLoadKeyToUse);
return nextLoad;
}
public static Properties loadPropertiesFromInputStream(InputStream fin) throws IOException {
Properties props = new Properties();
InputStreamReader reader = new InputStreamReader(fin, "UTF-8");
try {
props.load(reader);
return props;
} finally {
if (reader != null) {
reader.close();
}
if (fin != null) {
fin.close();
}
}
}
}
class OverridingPropertiesConfiguration extends PropertiesConfiguration {
public OverridingPropertiesConfiguration() {
super();
}
public OverridingPropertiesConfiguration(File file)
throws ConfigurationException {
super(file);
}
public OverridingPropertiesConfiguration(String fileName)
throws ConfigurationException {
super(fileName);
}
public OverridingPropertiesConfiguration(URL url)
throws ConfigurationException {
super(url);
}
@Override
public void addProperty(String name, Object value) {
if (containsKey(name)) {
clearPropertyDirect(name);
}
super.addProperty(name, value);
}
}