/*
* Copyright 2010 Proofpoint, 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 io.airlift.configuration;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.inject.Key;
import io.airlift.configuration.ConfigurationMetadata.AttributeMetadata;
import java.lang.reflect.Method;
import java.util.SortedSet;
import static com.google.common.base.MoreObjects.toStringHelper;
import static java.util.Objects.requireNonNull;
import static io.airlift.configuration.ConfigurationMetadata.getConfigurationMetadata;
public class ConfigurationInspector
{
public SortedSet<ConfigRecord<?>> inspect(ConfigurationFactory configurationFactory)
{
ImmutableSortedSet.Builder<ConfigRecord<?>> builder = ImmutableSortedSet.naturalOrder();
for (ConfigurationProvider<?> configurationProvider : configurationFactory.getConfigurationProviders()) {
ConfigRecord<?> result = new ConfigRecord<>(configurationFactory, configurationProvider);
builder.add(result);
}
return builder.build();
}
private static String getValue(Method getter, Object instance, String defaultValue)
{
if (getter == null || instance == null) {
return defaultValue;
}
try {
Object value = getter.invoke(instance);
if (value == null) {
return "null";
}
return value.toString();
}
catch (Throwable e) {
return "-- ERROR --";
}
}
public static class ConfigRecord<T>
implements Comparable<ConfigRecord<?>>
{
private final Key<T> key;
private final Class<T> configClass;
private final String prefix;
private final SortedSet<ConfigAttribute> attributes;
private ConfigRecord(ConfigurationFactory configurationFactory, ConfigurationProvider<T> configurationProvider)
{
requireNonNull(configurationProvider, "configurationProvider");
ConfigurationBinding<T> configurationBinding = configurationProvider.getConfigurationBinding();
key = configurationBinding.getKey();
configClass = configurationBinding.getConfigClass();
prefix = configurationBinding.getPrefix().orElse(null);
ConfigurationMetadata<T> metadata = getConfigurationMetadata(configurationBinding.getConfigClass());
T defaults = configurationFactory.getDefaultConfig(configurationBinding.getKey());
T instance = null;
try {
instance = configurationProvider.get();
}
catch (Throwable ignored) {
// provider could blow up for any reason, which is fine for this code
// this is catch throwable because we may get an AssertionError
}
String prefix = configurationBinding.getPrefix()
.map(value -> value + ".")
.orElse("");
ImmutableSortedSet.Builder<ConfigAttribute> builder = ImmutableSortedSet.naturalOrder();
for (AttributeMetadata attribute : metadata.getAttributes().values()) {
String propertyName = prefix + attribute.getInjectionPoint().getProperty();
Method getter = attribute.getGetter();
String defaultValue = getValue(getter, defaults, "-- none --");
String currentValue = getValue(getter, instance, "-- n/a --");
String description = attribute.getDescription();
if (description == null) {
description = "";
}
builder.add(new ConfigAttribute(attribute.getName(), propertyName, defaultValue, currentValue, description));
}
attributes = builder.build();
}
public Key<T> getKey()
{
return key;
}
public Class<T> getConfigClass()
{
return configClass;
}
public String getPrefix()
{
return prefix;
}
public SortedSet<ConfigAttribute> getAttributes()
{
return attributes;
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConfigRecord<?> that = (ConfigRecord<?>) o;
return key.equals(that.key);
}
@Override
public int hashCode()
{
return key.hashCode();
}
@Override
public int compareTo(ConfigRecord<?> that)
{
return ComparisonChain.start()
.compare(String.valueOf(this.key.getTypeLiteral().getType()), String.valueOf(that.key.getTypeLiteral().getType()))
.compare(String.valueOf(this.key.getAnnotationType()), String.valueOf(that.key.getAnnotationType()))
.compare(this.key, that.key, Ordering.arbitrary())
.result();
}
}
public static class ConfigAttribute
implements Comparable<ConfigAttribute>
{
private final String attributeName;
private final String propertyName;
private final String defaultValue;
private final String currentValue;
private final String description;
// todo this class needs to be updated to include the concept of deprecated property names
private ConfigAttribute(String attributeName, String propertyName, String defaultValue, String currentValue, String description)
{
requireNonNull(attributeName, "attributeName");
requireNonNull(propertyName, "propertyName");
requireNonNull(defaultValue, "defaultValue");
requireNonNull(currentValue, "currentValue");
requireNonNull(description, "description");
this.attributeName = attributeName;
this.propertyName = propertyName;
this.defaultValue = defaultValue;
this.currentValue = currentValue;
this.description = description;
}
public String getAttributeName()
{
return attributeName;
}
public String getPropertyName()
{
return propertyName;
}
public String getDefaultValue()
{
return defaultValue;
}
public String getCurrentValue()
{
return currentValue;
}
public String getDescription()
{
return description;
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConfigAttribute that = (ConfigAttribute) o;
return attributeName.equals(that.attributeName);
}
@Override
public int hashCode()
{
return attributeName.hashCode();
}
@Override
public int compareTo(ConfigAttribute that)
{
return this.attributeName.compareTo(that.attributeName);
}
@Override
public String toString()
{
return toStringHelper(this)
.add("attributeName", attributeName)
.add("propertyName", propertyName)
.add("defaultValue", defaultValue)
.add("currentValue", currentValue)
.add("description", description)
.toString();
}
}
}