org.apache.brooklyn.core.config.BasicConfigKey.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.core.config.BasicConfigKey.java

Source

/*
 * 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.brooklyn.core.config;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.brooklyn.api.mgmt.ExecutionContext;
import org.apache.brooklyn.config.ConfigInheritance;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.util.core.internal.ConfigKeySelfExtracting;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.TypeTokens;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;

public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializable {

    private static final Logger log = LoggerFactory.getLogger(BasicConfigKey.class);
    private static final long serialVersionUID = -1762014059150215376L;

    private static final Splitter dots = Splitter.on('.');

    public static <T> Builder<T> builder(TypeToken<T> type) {
        return new Builder<T>().type(type);
    }

    public static <T> Builder<T> builder(Class<T> type) {
        return new Builder<T>().type(type);
    }

    public static <T> Builder<T> builder(TypeToken<T> type, String name) {
        return new Builder<T>().type(type).name(name);
    }

    public static <T> Builder<T> builder(Class<T> type, String name) {
        return new Builder<T>().type(type).name(name);
    }

    public static <T> Builder<T> builder(ConfigKey<T> key) {
        return new Builder<T>().name(checkNotNull(key.getName(), "name"))
                .type(checkNotNull(key.getTypeToken(), "type")).description(key.getDescription())
                .defaultValue(key.getDefaultValue()).reconfigurable(key.isReconfigurable())
                .inheritance(key.getInheritance()).constraint(key.getConstraint());
    }

    public static class Builder<T> {
        private String name;
        private TypeToken<T> type;
        private String description;
        private T defaultValue;
        private boolean reconfigurable;
        private Predicate<? super T> constraint = Predicates.alwaysTrue();
        private ConfigInheritance inheritance;

        public Builder<T> name(String val) {
            this.name = val;
            return this;
        }

        public Builder<T> type(Class<T> val) {
            this.type = TypeToken.of(val);
            return this;
        }

        public Builder<T> type(TypeToken<T> val) {
            this.type = val;
            return this;
        }

        public Builder<T> description(String val) {
            this.description = val;
            return this;
        }

        public Builder<T> defaultValue(T val) {
            this.defaultValue = val;
            return this;
        }

        public Builder<T> reconfigurable(boolean val) {
            this.reconfigurable = val;
            return this;
        }

        public Builder<T> inheritance(ConfigInheritance val) {
            this.inheritance = val;
            return this;
        }

        @Beta
        public Builder<T> constraint(Predicate<? super T> constraint) {
            this.constraint = checkNotNull(constraint, "constraint");
            return this;
        }

        public BasicConfigKey<T> build() {
            return new BasicConfigKey<T>(this);
        }

        public String getName() {
            return name;
        }

        public String getDescription() {
            return description;
        }
    }

    private String name;
    private TypeToken<T> typeToken;
    private Class<? super T> type;
    private String description;
    private T defaultValue;
    private boolean reconfigurable;
    private ConfigInheritance inheritance;
    private Predicate<? super T> constraint;

    // FIXME In groovy, fields were `public final` with a default constructor; do we need the gson?
    public BasicConfigKey() {
        /* for gson */ }

    public BasicConfigKey(Class<T> type, String name) {
        this(TypeToken.of(type), name);
    }

    public BasicConfigKey(Class<T> type, String name, String description) {
        this(TypeToken.of(type), name, description);
    }

    public BasicConfigKey(Class<T> type, String name, String description, T defaultValue) {
        this(TypeToken.of(type), name, description, defaultValue);
    }

    public BasicConfigKey(TypeToken<T> type, String name) {
        this(type, name, name, null);
    }

    public BasicConfigKey(TypeToken<T> type, String name, String description) {
        this(type, name, description, null);
    }

    public BasicConfigKey(TypeToken<T> type, String name, String description, T defaultValue) {
        this.description = description;
        this.name = checkNotNull(name, "name");

        this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(type, "type"));
        this.typeToken = TypeTokens.getTypeTokenIfNotRaw(type);

        this.defaultValue = defaultValue;
        this.reconfigurable = false;
        this.constraint = Predicates.alwaysTrue();
    }

    public BasicConfigKey(Builder<T> builder) {
        this.name = checkNotNull(builder.name, "name");
        this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(builder.type, "type"));
        this.typeToken = TypeTokens.getTypeTokenIfNotRaw(builder.type);
        this.description = builder.description;
        this.defaultValue = builder.defaultValue;
        this.reconfigurable = builder.reconfigurable;
        this.inheritance = builder.inheritance;
        // Note: it's intentionally possible to have default values that are not valid
        // per the configured constraint. If validity were checked here any class that
        // contained a weirdly-defined config key would fail to initialise.
        this.constraint = checkNotNull(builder.constraint, "constraint");
    }

    /** @see ConfigKey#getName() */
    @Override
    public String getName() {
        return name;
    }

    /** @see ConfigKey#getTypeName() */
    @Override
    public String getTypeName() {
        return getType().getName();
    }

    /** @see ConfigKey#getType() */
    @Override
    public Class<? super T> getType() {
        return TypeTokens.getRawType(typeToken, type);
    }

    /** @see ConfigKey#getTypeToken() */
    @Override
    public TypeToken<T> getTypeToken() {
        return TypeTokens.getTypeToken(typeToken, type);
    }

    /** @see ConfigKey#getDescription() */
    @Override
    public String getDescription() {
        return description;
    }

    /** @see ConfigKey#getDefaultValue() */
    @Override
    public T getDefaultValue() {
        return defaultValue;
    }

    /** @see ConfigKey#hasDefaultValue() */
    @Override
    public boolean hasDefaultValue() {
        return defaultValue != null;
    }

    /** @see ConfigKey#isReconfigurable() */
    @Override
    public boolean isReconfigurable() {
        return reconfigurable;
    }

    /** @see ConfigKey#getInheritance() */
    @Override
    @Nullable
    public ConfigInheritance getInheritance() {
        return inheritance;
    }

    /** @see ConfigKey#getConstraint() */
    @Override
    @Nonnull
    public Predicate<? super T> getConstraint() {
        // Could be null after rebinding
        if (constraint != null) {
            return constraint;
        } else {
            return Predicates.alwaysTrue();
        }
    }

    /** @see ConfigKey#isValueValid(T) */
    @Override
    public boolean isValueValid(T value) {
        // The likeliest source of an exception is a constraint from Guava that expects a non-null input.
        try {
            return getConstraint().apply(value);
        } catch (Exception e) {
            log.debug("Suppressing exception when testing validity of " + this, e);
            return false;
        }
    }

    /** @see ConfigKey#getNameParts() */
    @Override
    public Collection<String> getNameParts() {
        return Lists.newArrayList(dots.split(name));
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof BasicConfigKey))
            return false;
        BasicConfigKey<?> o = (BasicConfigKey<?>) obj;

        return Objects.equal(name, o.name);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(name);
    }

    @Override
    public String toString() {
        return String.format("%s[ConfigKey:%s]", name, getTypeName());
    }

    /**
     * Retrieves the value corresponding to this config key from the given map.
     * Could be overridden by more sophisticated config keys, such as MapConfigKey etc.
     */
    @SuppressWarnings("unchecked")
    @Override
    public T extractValue(Map<?, ?> vals, ExecutionContext exec) {
        Object v = vals.get(this);
        try {
            return (T) resolveValue(v, exec);
        } catch (Exception e) {
            throw Exceptions.propagate(e);
        }
    }

    @Override
    public boolean isSet(Map<?, ?> vals) {
        return vals.containsKey(this);
    }

    protected Object resolveValue(Object v, ExecutionContext exec) throws ExecutionException, InterruptedException {
        if (v instanceof Collection || v instanceof Map) {
            return Tasks.resolveDeepValue(v, Object.class, exec, "config " + name);
        } else {
            return Tasks.resolveValue(v, getType(), exec, "config " + name);
        }
    }

    /** used to record a key which overwrites another; only needed at disambiguation time 
     * if a class declares a key and an equivalent one (often inherited) which overwrites it.
     * See org.apache.brooklyn.core.entity.ConfigEntityInheritanceTest, and uses of this class, for more explanation.
     */
    public static class BasicConfigKeyOverwriting<T> extends BasicConfigKey<T> {
        private static final long serialVersionUID = -3458116971918128018L;

        private final ConfigKey<T> parentKey;

        /** builder here should be based on the same key passed in as parent */
        @Beta
        public BasicConfigKeyOverwriting(Builder<T> builder, ConfigKey<T> parent) {
            super(builder);
            parentKey = parent;
            Preconditions.checkArgument(Objects.equal(builder.name, parent.getName()),
                    "Builder must use key of the same name.");
        }

        public BasicConfigKeyOverwriting(ConfigKey<T> key, T defaultValue) {
            this(builder(key).defaultValue(defaultValue), key);
        }

        public BasicConfigKeyOverwriting(ConfigKey<T> key, String newDescription, T defaultValue) {
            this(builder(key).description(newDescription).defaultValue(defaultValue), key);
        }

        public ConfigKey<T> getParentKey() {
            return parentKey;
        }
    }
}