All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bookkeeper.common.conf.ConfigKey Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.bookkeeper.common.conf;

import static com.google.common.base.Preconditions.checkArgument;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import lombok.Builder.Default;
import org.apache.bookkeeper.common.annotation.InterfaceAudience.Public;
import org.apache.bookkeeper.common.conf.validators.NullValidator;
import org.apache.bookkeeper.common.util.ReflectionUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;

/**
 * A configuration key in a configuration.
 */
@Public
public class ConfigKey {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConfigKey.class);
    public static final Comparator ORDERING = (o1, o2) -> {
        int ret = Integer.compare(o1.orderInGroup, o2.orderInGroup);
        if (ret == 0) {
            return o1.name().compareTo(o2.name());
        } else {
            return ret;
        }
    };

    /**
     * Build a config key of name.
     *
     * @param name config key name
     * @return config key builder
     */
    public static ConfigKeyBuilder builder(String name) {
        return internalBuilder().name(name);
    }

    /**
     * Flag indicates whether the setting is required.
     */
    private boolean required;
    /**
     * Name of the configuration setting.
     */
    private String name;
    /**
     * Type of the configuration setting.
     */
    private Type type;
    /**
     * Description of the configuration setting.
     */
    private String description;
    /**
     * Documentation of the configuration setting.
     */
    private String documentation;
    /**
     * Default value as a string representation.
     */
    private Object defaultValue;

    private String defaultValueAsString() {
        if (null == defaultValue) {
            return null;
        } else if (defaultValue instanceof String) {
            return (String) defaultValue;
        } else if (defaultValue instanceof Class) {
            return ((Class) defaultValue).getName();
        } else {
            return defaultValue.toString();
        }
    }

    /**
     * The list of options for this setting.
     */
    private List optionValues;
    /**
     * The validator used for validating configuration value.
     */
    private Validator validator;
    /**
     * The key-group to group settings together.
     */
    private ConfigKeyGroup group;
    /**
     * The order of the setting in the key-group.
     */
    private int orderInGroup;
    /**
     * The list of settings dependents on this setting.
     */
    private List dependents;
    /**
     * Whether this setting is deprecated or not.
     */
    private boolean deprecated;
    /**
     * The config key that deprecates this key.
     */
    private String deprecatedByConfigKey;
    /**
     * The version when this settings was deprecated.
     */
    private String deprecatedSince;
    /**
     * The version when this setting was introduced.
     */
    private String since;

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof ConfigKey)) {
            return false;
        }
        ConfigKey other = (ConfigKey) o;
        return Objects.equals(name, other.name);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return name.hashCode();
    }

    /**
     * Validate the setting is valid in the provided config conf.
     *
     * @param conf configuration to test
     */
    public void validate(Configuration conf) throws ConfigException {
        if (conf.containsKey(name()) && validator() != null) {
            Object value = get(conf);
            if (!validator().validate(name(), value)) {
                throw new ConfigException("Invalid setting of \'" + name() + "\' found the configuration: value = \'" + value + "\', requirement = \'" + validator + "\'");
            }
        } else if (required()) {
            // missing config on a required field
            throw new ConfigException("Setting \'" + name() + "\' is required but missing in the configuration");
        }
    }

    /**
     * Update the setting name in the configuration conf with the provided value.
     *
     * @param conf configuration to set
     * @param value value of the setting
     */
    public void set(Configuration conf, Object value) {
        if (!type().validator().validate(name(), value)) {
            throw new IllegalArgumentException("Invalid value \'" + value + "\' to set on setting \'" + name() + "\': expected type = " + type);
        }
        if (null != validator() && !validator().validate(name(), value)) {
            throw new IllegalArgumentException("Invalid value \'" + value + "\' to set on setting \'" + name() + "\': required \'" + validator() + "\'");
        }
        if (value instanceof Class) {
            conf.setProperty(name(), ((Class) value).getName());
        } else {
            conf.setProperty(name(), value);
        }
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Long} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a long number
     */
    public long getLong(Configuration conf) {
        checkArgument(type() == Type.LONG, "\'" + name() + "\' is NOT a LONG numeric setting");
        return conf.getLong(name(), (Long) defaultValue());
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Integer} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as an integer number
     */
    public int getInt(Configuration conf) {
        checkArgument(type() == Type.INT, "\'" + name() + "\' is NOT a INT numeric setting");
        return conf.getInt(name(), (Integer) defaultValue());
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Short} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a short number
     */
    public short getShort(Configuration conf) {
        checkArgument(type() == Type.SHORT, "\'" + name() + "\' is NOT a SHORT numeric setting");
        return conf.getShort(name(), (Short) defaultValue());
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Boolean} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a boolean flag
     */
    public boolean getBoolean(Configuration conf) {
        checkArgument(type() == Type.BOOLEAN, "\'" + name() + "\' is NOT a BOOL numeric setting");
        return conf.getBoolean(name(), (Boolean) defaultValue());
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Double} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a double number
     */
    public double getDouble(Configuration conf) {
        checkArgument(type() == Type.DOUBLE, "\'" + name() + "\' is NOT a DOUBLE numeric setting");
        return conf.getDouble(name(), (Double) defaultValue());
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link String} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a string.
     */
    public String getString(Configuration conf) {
        return conf.getString(name(), defaultValueAsString());
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Class} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a class
     */
    @SuppressWarnings("unchecked")
    public  Class getClass(Configuration conf, Class interfaceCls) {
        checkArgument(type() == Type.CLASS, "\'" + name() + "\' is NOT a CLASS setting");
        try {
            Class defaultClass = (Class) defaultValue();
            return ReflectionUtils.getClass(conf, name(), defaultClass, interfaceCls, getClass().getClassLoader());
        } catch (ConfigurationException e) {
            throw new IllegalArgumentException("Invalid class is set to setting \'" + name() + "\': ", e);
        }
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Class} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as a class
     */
    @SuppressWarnings("unchecked")
    public Class getClass(Configuration conf) {
        checkArgument(type() == Type.CLASS, "\'" + name() + "\' is NOT a CLASS setting");
        try {
            Class defaultClass = (Class) defaultValue();
            return ReflectionUtils.getClass(conf, name(), defaultClass, getClass().getClassLoader());
        } catch (ConfigurationException e) {
            throw new IllegalArgumentException("Invalid class is set to setting \'" + name() + "\': ", e);
        }
    }

    /**
     * Retrieve the setting from the configuration conf as a {@link Class} value.
     *
     * @param conf configuration to retrieve the setting
     * @return the value as list of values
     */
    @SuppressWarnings("unchecked")
    public List getList(Configuration conf) {
        checkArgument(type() == Type.LIST, "\'" + name() + "\' is NOT a LIST setting");
        List list = (List) defaultValue();
        if (null == list) {
            list = Collections.emptyList();
        }
        return conf.getList(name(), list);
    }

    /**
     * Retrieve the setting value from the provided conf.
     *
     * @return the setting value
     */
    public Object get(Configuration conf) {
        switch (type()) {
        case LONG: 
            return getLong(conf);
        case INT: 
            return getInt(conf);
        case SHORT: 
            return getShort(conf);
        case DOUBLE: 
            return getDouble(conf);
        case BOOLEAN: 
            return getBoolean(conf);
        case LIST: 
            return getList(conf);
        case CLASS: 
            return getClass(conf);
        default: 
            return getString(conf);
        }
    }

    private static boolean $default$required() {
        return false;
    }

    private static Type $default$type() {
        return Type.STRING;
    }

    private static String $default$description() {
        return "";
    }

    private static String $default$documentation() {
        return "";
    }

    private static Object $default$defaultValue() {
        return null;
    }

    private static List $default$optionValues() {
        return Collections.emptyList();
    }

    private static Validator $default$validator() {
        return NullValidator.of();
    }

    private static ConfigKeyGroup $default$group() {
        return ConfigKeyGroup.DEFAULT;
    }

    private static int $default$orderInGroup() {
        return Integer.MIN_VALUE;
    }

    private static List $default$dependents() {
        return Collections.emptyList();
    }

    private static boolean $default$deprecated() {
        return false;
    }

    private static String $default$deprecatedByConfigKey() {
        return "";
    }

    private static String $default$deprecatedSince() {
        return "";
    }

    private static String $default$since() {
        return "";
    }

    ConfigKey(final boolean required, final String name, final Type type, final String description, final String documentation, final Object defaultValue, final List optionValues, final Validator validator, final ConfigKeyGroup group, final int orderInGroup, final List dependents, final boolean deprecated, final String deprecatedByConfigKey, final String deprecatedSince, final String since) {
        this.required = required;
        this.name = name;
        this.type = type;
        this.description = description;
        this.documentation = documentation;
        this.defaultValue = defaultValue;
        this.optionValues = optionValues;
        this.validator = validator;
        this.group = group;
        this.orderInGroup = orderInGroup;
        this.dependents = dependents;
        this.deprecated = deprecated;
        this.deprecatedByConfigKey = deprecatedByConfigKey;
        this.deprecatedSince = deprecatedSince;
        this.since = since;
    }


    public static class ConfigKeyBuilder {
        private boolean required$set;
        private boolean required$value;
        private String name;
        private boolean type$set;
        private Type type$value;
        private boolean description$set;
        private String description$value;
        private boolean documentation$set;
        private String documentation$value;
        private boolean defaultValue$set;
        private Object defaultValue$value;
        private boolean optionValues$set;
        private List optionValues$value;
        private boolean validator$set;
        private Validator validator$value;
        private boolean group$set;
        private ConfigKeyGroup group$value;
        private boolean orderInGroup$set;
        private int orderInGroup$value;
        private boolean dependents$set;
        private List dependents$value;
        private boolean deprecated$set;
        private boolean deprecated$value;
        private boolean deprecatedByConfigKey$set;
        private String deprecatedByConfigKey$value;
        private boolean deprecatedSince$set;
        private String deprecatedSince$value;
        private boolean since$set;
        private String since$value;

        ConfigKeyBuilder() {
        }

        /**
         * Flag indicates whether the setting is required.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder required(final boolean required) {
            this.required$value = required;
            required$set = true;
            return this;
        }

        /**
         * Name of the configuration setting.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder name(final String name) {
            this.name = name;
            return this;
        }

        /**
         * Type of the configuration setting.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder type(final Type type) {
            this.type$value = type;
            type$set = true;
            return this;
        }

        /**
         * Description of the configuration setting.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder description(final String description) {
            this.description$value = description;
            description$set = true;
            return this;
        }

        /**
         * Documentation of the configuration setting.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder documentation(final String documentation) {
            this.documentation$value = documentation;
            documentation$set = true;
            return this;
        }

        /**
         * Default value as a string representation.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder defaultValue(final Object defaultValue) {
            this.defaultValue$value = defaultValue;
            defaultValue$set = true;
            return this;
        }

        /**
         * The list of options for this setting.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder optionValues(final List optionValues) {
            this.optionValues$value = optionValues;
            optionValues$set = true;
            return this;
        }

        /**
         * The validator used for validating configuration value.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder validator(final Validator validator) {
            this.validator$value = validator;
            validator$set = true;
            return this;
        }

        /**
         * The key-group to group settings together.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder group(final ConfigKeyGroup group) {
            this.group$value = group;
            group$set = true;
            return this;
        }

        /**
         * The order of the setting in the key-group.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder orderInGroup(final int orderInGroup) {
            this.orderInGroup$value = orderInGroup;
            orderInGroup$set = true;
            return this;
        }

        /**
         * The list of settings dependents on this setting.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder dependents(final List dependents) {
            this.dependents$value = dependents;
            dependents$set = true;
            return this;
        }

        /**
         * Whether this setting is deprecated or not.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder deprecated(final boolean deprecated) {
            this.deprecated$value = deprecated;
            deprecated$set = true;
            return this;
        }

        /**
         * The config key that deprecates this key.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder deprecatedByConfigKey(final String deprecatedByConfigKey) {
            this.deprecatedByConfigKey$value = deprecatedByConfigKey;
            deprecatedByConfigKey$set = true;
            return this;
        }

        /**
         * The version when this settings was deprecated.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder deprecatedSince(final String deprecatedSince) {
            this.deprecatedSince$value = deprecatedSince;
            deprecatedSince$set = true;
            return this;
        }

        /**
         * The version when this setting was introduced.
         * @return {@code this}.
         */
        public ConfigKey.ConfigKeyBuilder since(final String since) {
            this.since$value = since;
            since$set = true;
            return this;
        }

        public ConfigKey build() {
            boolean required$value = this.required$value;
            if (!this.required$set) required$value = ConfigKey.$default$required();
            Type type$value = this.type$value;
            if (!this.type$set) type$value = ConfigKey.$default$type();
            String description$value = this.description$value;
            if (!this.description$set) description$value = ConfigKey.$default$description();
            String documentation$value = this.documentation$value;
            if (!this.documentation$set) documentation$value = ConfigKey.$default$documentation();
            Object defaultValue$value = this.defaultValue$value;
            if (!this.defaultValue$set) defaultValue$value = ConfigKey.$default$defaultValue();
            List optionValues$value = this.optionValues$value;
            if (!this.optionValues$set) optionValues$value = ConfigKey.$default$optionValues();
            Validator validator$value = this.validator$value;
            if (!this.validator$set) validator$value = ConfigKey.$default$validator();
            ConfigKeyGroup group$value = this.group$value;
            if (!this.group$set) group$value = ConfigKey.$default$group();
            int orderInGroup$value = this.orderInGroup$value;
            if (!this.orderInGroup$set) orderInGroup$value = ConfigKey.$default$orderInGroup();
            List dependents$value = this.dependents$value;
            if (!this.dependents$set) dependents$value = ConfigKey.$default$dependents();
            boolean deprecated$value = this.deprecated$value;
            if (!this.deprecated$set) deprecated$value = ConfigKey.$default$deprecated();
            String deprecatedByConfigKey$value = this.deprecatedByConfigKey$value;
            if (!this.deprecatedByConfigKey$set) deprecatedByConfigKey$value = ConfigKey.$default$deprecatedByConfigKey();
            String deprecatedSince$value = this.deprecatedSince$value;
            if (!this.deprecatedSince$set) deprecatedSince$value = ConfigKey.$default$deprecatedSince();
            String since$value = this.since$value;
            if (!this.since$set) since$value = ConfigKey.$default$since();
            return new ConfigKey(required$value, this.name, type$value, description$value, documentation$value, defaultValue$value, optionValues$value, validator$value, group$value, orderInGroup$value, dependents$value, deprecated$value, deprecatedByConfigKey$value, deprecatedSince$value, since$value);
        }

        @Override
        public String toString() {
            return "ConfigKey.ConfigKeyBuilder(required$value=" + this.required$value + ", name=" + this.name + ", type$value=" + this.type$value + ", description$value=" + this.description$value + ", documentation$value=" + this.documentation$value + ", defaultValue$value=" + this.defaultValue$value + ", optionValues$value=" + this.optionValues$value + ", validator$value=" + this.validator$value + ", group$value=" + this.group$value + ", orderInGroup$value=" + this.orderInGroup$value + ", dependents$value=" + this.dependents$value + ", deprecated$value=" + this.deprecated$value + ", deprecatedByConfigKey$value=" + this.deprecatedByConfigKey$value + ", deprecatedSince$value=" + this.deprecatedSince$value + ", since$value=" + this.since$value + ")";
        }
    }

    public static ConfigKey.ConfigKeyBuilder internalBuilder() {
        return new ConfigKey.ConfigKeyBuilder();
    }

    /**
     * Flag indicates whether the setting is required.
     */
    public boolean required() {
        return this.required;
    }

    /**
     * Name of the configuration setting.
     */
    public String name() {
        return this.name;
    }

    /**
     * Type of the configuration setting.
     */
    public Type type() {
        return this.type;
    }

    /**
     * Description of the configuration setting.
     */
    public String description() {
        return this.description;
    }

    /**
     * Documentation of the configuration setting.
     */
    public String documentation() {
        return this.documentation;
    }

    /**
     * Default value as a string representation.
     */
    public Object defaultValue() {
        return this.defaultValue;
    }

    /**
     * The list of options for this setting.
     */
    public List optionValues() {
        return this.optionValues;
    }

    /**
     * The validator used for validating configuration value.
     */
    public Validator validator() {
        return this.validator;
    }

    /**
     * The key-group to group settings together.
     */
    public ConfigKeyGroup group() {
        return this.group;
    }

    /**
     * The order of the setting in the key-group.
     */
    public int orderInGroup() {
        return this.orderInGroup;
    }

    /**
     * The list of settings dependents on this setting.
     */
    public List dependents() {
        return this.dependents;
    }

    /**
     * Whether this setting is deprecated or not.
     */
    public boolean deprecated() {
        return this.deprecated;
    }

    /**
     * The config key that deprecates this key.
     */
    public String deprecatedByConfigKey() {
        return this.deprecatedByConfigKey;
    }

    /**
     * The version when this settings was deprecated.
     */
    public String deprecatedSince() {
        return this.deprecatedSince;
    }

    /**
     * The version when this setting was introduced.
     */
    public String since() {
        return this.since;
    }

    /**
     * Flag indicates whether the setting is required.
     * @return {@code this}.
     */
    public ConfigKey required(final boolean required) {
        this.required = required;
        return this;
    }

    /**
     * Name of the configuration setting.
     * @return {@code this}.
     */
    public ConfigKey name(final String name) {
        this.name = name;
        return this;
    }

    /**
     * Type of the configuration setting.
     * @return {@code this}.
     */
    public ConfigKey type(final Type type) {
        this.type = type;
        return this;
    }

    /**
     * Description of the configuration setting.
     * @return {@code this}.
     */
    public ConfigKey description(final String description) {
        this.description = description;
        return this;
    }

    /**
     * Documentation of the configuration setting.
     * @return {@code this}.
     */
    public ConfigKey documentation(final String documentation) {
        this.documentation = documentation;
        return this;
    }

    /**
     * Default value as a string representation.
     * @return {@code this}.
     */
    public ConfigKey defaultValue(final Object defaultValue) {
        this.defaultValue = defaultValue;
        return this;
    }

    /**
     * The list of options for this setting.
     * @return {@code this}.
     */
    public ConfigKey optionValues(final List optionValues) {
        this.optionValues = optionValues;
        return this;
    }

    /**
     * The validator used for validating configuration value.
     * @return {@code this}.
     */
    public ConfigKey validator(final Validator validator) {
        this.validator = validator;
        return this;
    }

    /**
     * The key-group to group settings together.
     * @return {@code this}.
     */
    public ConfigKey group(final ConfigKeyGroup group) {
        this.group = group;
        return this;
    }

    /**
     * The order of the setting in the key-group.
     * @return {@code this}.
     */
    public ConfigKey orderInGroup(final int orderInGroup) {
        this.orderInGroup = orderInGroup;
        return this;
    }

    /**
     * The list of settings dependents on this setting.
     * @return {@code this}.
     */
    public ConfigKey dependents(final List dependents) {
        this.dependents = dependents;
        return this;
    }

    /**
     * Whether this setting is deprecated or not.
     * @return {@code this}.
     */
    public ConfigKey deprecated(final boolean deprecated) {
        this.deprecated = deprecated;
        return this;
    }

    /**
     * The config key that deprecates this key.
     * @return {@code this}.
     */
    public ConfigKey deprecatedByConfigKey(final String deprecatedByConfigKey) {
        this.deprecatedByConfigKey = deprecatedByConfigKey;
        return this;
    }

    /**
     * The version when this settings was deprecated.
     * @return {@code this}.
     */
    public ConfigKey deprecatedSince(final String deprecatedSince) {
        this.deprecatedSince = deprecatedSince;
        return this;
    }

    /**
     * The version when this setting was introduced.
     * @return {@code this}.
     */
    public ConfigKey since(final String since) {
        this.since = since;
        return this;
    }

    @Override
    public String toString() {
        return "ConfigKey(required=" + this.required() + ", name=" + this.name() + ", type=" + this.type() + ", description=" + this.description() + ", documentation=" + this.documentation() + ", defaultValue=" + this.defaultValue() + ", optionValues=" + this.optionValues() + ", validator=" + this.validator() + ", group=" + this.group() + ", orderInGroup=" + this.orderInGroup() + ", dependents=" + this.dependents() + ", deprecated=" + this.deprecated() + ", deprecatedByConfigKey=" + this.deprecatedByConfigKey() + ", deprecatedSince=" + this.deprecatedSince() + ", since=" + this.since() + ")";
    }
}