/*******************************************************************************
* /***
* *
* * Copyright 2013 Netflix, Inc.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *
******************************************************************************/
package com.netflix.paas.config.base;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Maps;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.PropertyWrapper;
import com.netflix.governator.annotations.Configuration;
import com.netflix.paas.config.annotations.DefaultValue;
import com.netflix.paas.config.annotations.Dynamic;
import java.lang.reflect.Field;
import org.apache.commons.configuration.AbstractConfiguration;
/**
* Utility class used by ConfigurationProxyFactory implementations to proxy methods of a
* configuration interface using information from the Configuration annotation
*
* @author elandau
*/
public class ConfigurationProxyUtils {
public static class PropertyWrapperSupplier<T> implements Supplier<T> {
private final PropertyWrapper<T> wrapper;
public PropertyWrapperSupplier(PropertyWrapper<T> wrapper) {
this.wrapper = wrapper;
}
@Override
public T get() {
return this.wrapper.getValue();
}
}
static Supplier<?> getDynamicSupplier(Class<?> type, String key, String defaultValue, DynamicPropertyFactory propertyFactory) {
if (type.isAssignableFrom(String.class)) {
return new PropertyWrapperSupplier<String>(
propertyFactory.getStringProperty(
key,
defaultValue));
}
else if (type.isAssignableFrom(Integer.class)) {
return new PropertyWrapperSupplier<Integer>(
propertyFactory.getIntProperty(
key,
defaultValue == null ? 0 : Integer.parseInt(defaultValue)));
}
else if (type.isAssignableFrom(Double.class)) {
return new PropertyWrapperSupplier<Double>(
propertyFactory.getDoubleProperty(
key,
defaultValue == null ? 0.0 : Double.parseDouble(defaultValue)));
}
else if (type.isAssignableFrom(Long.class)) {
return new PropertyWrapperSupplier<Long>(
propertyFactory.getLongProperty(
key,
defaultValue == null ? 0L : Long.parseLong(defaultValue)));
}
else if (type.isAssignableFrom(Boolean.class)) {
return new PropertyWrapperSupplier<Boolean>(
propertyFactory.getBooleanProperty(
key,
defaultValue == null ? false : Boolean.parseBoolean(defaultValue)));
}
throw new RuntimeException("Unsupported value type " + type.getCanonicalName());
}
static Supplier<?> getStaticSupplier(Class<?> type, String key, String defaultValue, AbstractConfiguration configuration) {
if (type.isAssignableFrom(String.class)) {
return Suppliers.ofInstance(
configuration.getString(
key,
defaultValue));
}
else if (type.isAssignableFrom(Integer.class)) {
return Suppliers.ofInstance(
configuration.getInteger(
key,
defaultValue == null ? 0 : Integer.parseInt(defaultValue)));
}
else if (type.isAssignableFrom(Double.class)) {
return Suppliers.ofInstance(
configuration.getDouble(
key,
defaultValue == null ? 0.0 : Double.parseDouble(defaultValue)));
}
else if (type.isAssignableFrom(Long.class)) {
return Suppliers.ofInstance(
configuration.getLong(
key,
defaultValue == null ? 0L : Long.parseLong(defaultValue)));
}
else if (type.isAssignableFrom(Boolean.class)) {
return Suppliers.ofInstance(
configuration.getBoolean(
key,
defaultValue == null ? false : Boolean.parseBoolean(defaultValue)));
}
throw new RuntimeException("Unsupported value type " + type.getCanonicalName());
}
static String getPropertyName(Method method, Configuration c) {
String name = c.value();
if (name.isEmpty()) {
name = method.getName();
name = StringUtils.removeStart(name, "is");
name = StringUtils.removeStart(name, "get");
name = name.toLowerCase();
}
return name;
}
static String getPropertyName(Field field, Configuration c) {
String name = c.value();
if (name.isEmpty()) {
return field.getName();
}
return name;
}
static <T> Map<String, Supplier<?>> getMethodSuppliers(Class<T> configClass, DynamicPropertyFactory propertyFactory, AbstractConfiguration configuration) {
final Map<String, Supplier<?>> properties = Maps.newHashMap();
for (Method method : configClass.getMethods()) {
Configuration c = method.getAnnotation(Configuration.class);
if (c == null)
continue;
String defaultValue = null;
DefaultValue dv = method.getAnnotation(DefaultValue.class);
if (dv != null)
defaultValue = dv.value();
String name = getPropertyName(method, c);
if (method.getReturnType().isAssignableFrom(Supplier.class)) {
Type returnType = method.getGenericReturnType();
if(returnType instanceof ParameterizedType){
ParameterizedType type = (ParameterizedType) returnType;
Class<?> actualType = (Class<?>)type.getActualTypeArguments()[0];
properties.put(method.getName(),
method.getAnnotation(Dynamic.class) != null
? Suppliers.ofInstance(getDynamicSupplier(actualType, name, defaultValue, propertyFactory))
: Suppliers.ofInstance(getStaticSupplier(actualType, name, defaultValue, configuration)));
}
else {
throw new RuntimeException("We'll get to this later");
}
}
else {
properties.put(method.getName(),
method.getAnnotation(Dynamic.class) != null
? getDynamicSupplier(method.getReturnType(), name, defaultValue, propertyFactory)
: getStaticSupplier (method.getReturnType(), name, defaultValue, configuration));
}
}
return properties;
}
static void assignFieldValues(final Object obj, Class<?> type, DynamicPropertyFactory propertyFactory, AbstractConfiguration configuration) throws Exception {
// Iterate through all fields and set initial value as well as set up dynamic properties
// where necessary
for (final Field field : type.getFields()) {
Configuration c = field.getAnnotation(Configuration.class);
if (c == null)
continue;
String defaultValue = field.get(obj).toString();
String name = ConfigurationProxyUtils.getPropertyName(field, c);
Supplier<?> supplier = ConfigurationProxyUtils.getStaticSupplier(field.getType(), name, defaultValue, configuration);
field.set(obj, supplier.get());
if (field.getAnnotation(Dynamic.class) != null) {
final PropertyWrapper<?> property;
if (field.getType().isAssignableFrom(String.class)) {
property = propertyFactory.getStringProperty(
name,
defaultValue);
}
else if (field.getType().isAssignableFrom(Integer.class)) {
property = propertyFactory.getIntProperty(
name,
defaultValue == null ? 0 : Integer.parseInt(defaultValue));
}
else if (field.getType().isAssignableFrom(Double.class)) {
property = propertyFactory.getDoubleProperty(
name,
defaultValue == null ? 0.0 : Double.parseDouble(defaultValue));
}
else if (field.getType().isAssignableFrom(Long.class)) {
property = propertyFactory.getLongProperty(
name,
defaultValue == null ? 0L : Long.parseLong(defaultValue));
}
else if (field.getType().isAssignableFrom(Boolean.class)) {
property = propertyFactory.getBooleanProperty(
name,
defaultValue == null ? false : Boolean.parseBoolean(defaultValue));
}
else {
throw new RuntimeException("Unsupported type " + field.getType());
}
property.addCallback(new Runnable() {
@Override
public void run() {
try {
field.set(obj, property.getValue());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
}