package com.netflix.fabricator.archaius;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicProperty;
import com.netflix.fabricator.ConfigurationNode;
import com.netflix.fabricator.properties.AbstractPropertiesComponentConfiguration;
import com.netflix.fabricator.supplier.ListenableSupplier;
public class ArchaiusComponentConfiguration extends AbstractPropertiesComponentConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(ArchaiusComponentConfiguration.class);
public static ArchaiusComponentConfiguration forPrefix(String prefix) {
AbstractConfiguration config = ConfigurationManager.getConfigInstance();
String type = config.getString(prefix + "type");
String id = StringUtils.substringAfterLast(prefix, ".");
return new ArchaiusComponentConfiguration(id, type, config, prefix);
}
private final AbstractConfiguration config;
public ArchaiusComponentConfiguration(String id, String type, AbstractConfiguration config, String prefix) {
super(id, type, prefix);
this.config = config;
}
public ArchaiusComponentConfiguration(String id, String type, AbstractConfiguration config) {
super(id, type);
this.config = config;
}
public static abstract class DynamicListenableSupplier<T> implements ListenableSupplier<T> {
private final DynamicProperty prop;
DynamicListenableSupplier(DynamicProperty prop) {
this.prop = prop;
}
@Override
public void onChange(final Function<T, Void> func) {
prop.addCallback(new Runnable() {
@Override
public void run() {
func.apply(get());
}
});
}
}
@SuppressWarnings("unchecked")
@Override
public <T> ListenableSupplier<T> getDynamicValue(Class<T> type) {
final DynamicProperty prop = DynamicProperty.getInstance(getFullName());
if ( String.class.isAssignableFrom(type) ) {
return (ListenableSupplier<T>) new DynamicListenableSupplier<String>(prop) {
@Override
public String get() {
return prop.getString();
}
};
}
else if ( Boolean.class.isAssignableFrom(type) ||
Boolean.TYPE.isAssignableFrom(type) ||
boolean.class.equals(type)) {
return (ListenableSupplier<T>) new DynamicListenableSupplier<Boolean>(prop) {
@Override
public Boolean get() {
return prop.getBoolean();
}
};
}
else if ( Integer.class.isAssignableFrom(type)
|| Integer.TYPE.isAssignableFrom(type)
|| int.class.equals(type))
{
return (ListenableSupplier<T>) new DynamicListenableSupplier<Integer>(prop) {
@Override
public Integer get() {
return prop.getInteger();
}
};
}
else if ( Long.class.isAssignableFrom(type)
|| Long.TYPE.isAssignableFrom(type)
|| long.class.equals(type))
{
return (ListenableSupplier<T>) new DynamicListenableSupplier<Long>(prop) {
@Override
public Long get() {
return prop.getLong();
}
};
}
else if ( Double.class.isAssignableFrom(type)
|| Double.TYPE.isAssignableFrom(type)
|| double.class.equals(type))
{
return (ListenableSupplier<T>) new DynamicListenableSupplier<Double>(prop) {
@Override
public Double get() {
return prop.getDouble();
}
};
}
else if ( Properties.class.isAssignableFrom(type)) {
return (ListenableSupplier<T>) new DynamicListenableSupplier<Properties>(prop) {
@Override
public Properties get() {
if (config.containsKey(getFullName())) {
throw new RuntimeException(getFullName() + " is not a root for a properties structure");
}
String prefix = getFullName();
Properties result = new Properties();
for (String prop : Lists.newArrayList(config.getKeys(prefix))) {
result.setProperty(prop, config.getString(prop));
}
return result;
}
};
}
else {
LOG.warn(String.format("Unknown type '%s' for property '%s'", type.getCanonicalName(), getFullName()));
}
return null;
}
@Override
public ConfigurationNode getChild(String name) {
String fullName = Joiner.on(".").skipNulls().join(getFullName(), name);
return new ArchaiusComponentConfiguration(
name,
config.getString(Joiner.on(".").skipNulls().join(fullName, "type")),
config,
fullName);
}
@Override
public boolean isSingle() {
if (config.containsKey(getFullName())) {
return true;
}
return false; // TODO: Look for sub properties
}
@Override
public boolean hasChild(String propertyName) {
return true;
}
@Override
public Set<String> getUnknownProperties(Set<String> supportedProperties) {
// TODO:
return Collections.emptySet();
}
@SuppressWarnings("unchecked")
@Override
public <T> T getValue(Class<T> type) {
String key = getFullName();
if ( String.class.isAssignableFrom(type) ) {
return (T) config.getString(key);
}
else if ( Boolean.class.isAssignableFrom(type) ||
Boolean.TYPE.isAssignableFrom(type) ||
boolean.class.equals(type)) {
return (T) config.getBoolean(key, null);
}
else if ( Integer.class.isAssignableFrom(type)
|| Integer.TYPE.isAssignableFrom(type)
|| int.class.equals(type)) {
return (T) config.getInteger(key, null);
}
else if ( Long.class.isAssignableFrom(type)
|| Long.TYPE.isAssignableFrom(type)
|| long.class.equals(type)) {
return (T) config.getLong(key, null);
}
else if ( Double.class.isAssignableFrom(type)
|| Double.TYPE.isAssignableFrom(type)
|| double.class.equals(type)) {
return (T) config.getDouble(key, null);
}
else if ( Short.class.isAssignableFrom(type)
|| Short.TYPE.isAssignableFrom(type)
|| short.class.equals(type)) {
return (T) config.getShort(key, null);
}
else if ( Float.class.isAssignableFrom(type)
|| Float.TYPE.isAssignableFrom(type)
|| float.class.equals(type)) {
return (T) config.getFloat(key, null);
}
else if ( BigDecimal.class.isAssignableFrom(type) ) {
return (T) config.getBigDecimal(key, null);
}
else if ( BigInteger.class.isAssignableFrom(type) ) {
return (T) config.getBigInteger(key, null);
}
else if ( Properties.class.isAssignableFrom(type)) {
String prefix = getFullName();
Properties result = new Properties();
Configuration sub = config.subset(prefix);
Iterator<String> iter = sub.getKeys();
while (iter.hasNext()) {
String propName = iter.next();
result.setProperty(propName, sub.getString(propName));
}
return (T) result;
}
else {
LOG.warn(String.format("Unknown type '%s' for property '%s'", type.getCanonicalName(), getFullName()));
return null;
}
}
}