org.apache.accumulo.core.conf.SiteConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.core.conf.SiteConfiguration.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.accumulo.core.conf;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;

import org.apache.accumulo.core.util.CachedConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An {@link AccumuloConfiguration} which loads properties from an XML file, usually accumulo-site.xml. This implementation supports defaulting undefined
 * property values to a parent configuration's definitions.
 * <p>
 * The system property "org.apache.accumulo.config.file" can be used to specify the location of the XML configuration file on the classpath. If the system
 * property is not defined, it defaults to "accumulo-site.xml".
 * <p>
 * This class is a singleton.
 * <p>
 * <b>Note</b>: Client code should not use this class, and it may be deprecated in the future.
 */
public class SiteConfiguration extends AccumuloConfiguration {
    private static final Logger log = LoggerFactory.getLogger(SiteConfiguration.class);

    private AccumuloConfiguration parent = null;
    private static SiteConfiguration instance = null;

    private static Configuration xmlConfig;
    private final Map<String, String> staticConfigs;

    /**
     * Not for consumers. Call {@link SiteConfiguration#getInstance(AccumuloConfiguration)} instead
     */
    SiteConfiguration(AccumuloConfiguration parent) {
        this.parent = parent;
        /*
         * Make a read-only copy of static configs so we can avoid lock contention on the Hadoop Configuration object
         */
        final Configuration conf = getXmlConfig();
        Map<String, String> temp = new HashMap<>((int) (Math.ceil(conf.size() / 0.75f)), 0.75f);
        for (Entry<String, String> entry : conf) {
            temp.put(entry.getKey(), entry.getValue());
        }
        /*
         * If any of the configs used in hot codepaths are unset here, set a null so that we'll default to the parent config without contending for the Hadoop
         * Configuration object
         */
        for (Property hotConfig : Property.HOT_PATH_PROPERTIES) {
            if (!(temp.containsKey(hotConfig.getKey()))) {
                temp.put(hotConfig.getKey(), null);
            }
        }
        staticConfigs = Collections.unmodifiableMap(temp);
    }

    /**
     * Gets an instance of this class. A new instance is only created on the first call, and so the parent configuration cannot be changed later.
     *
     * @param parent
     *          parent (default) configuration
     * @throws RuntimeException
     *           if the configuration is invalid
     */
    synchronized public static SiteConfiguration getInstance(AccumuloConfiguration parent) {
        if (instance == null) {
            instance = new SiteConfiguration(parent);
            ConfigSanityCheck.validate(instance);
        }
        return instance;
    }

    synchronized public static SiteConfiguration getInstance() {
        return getInstance(DefaultConfiguration.getInstance());
    }

    synchronized private static Configuration getXmlConfig() {
        String configFile = System.getProperty("org.apache.accumulo.config.file", "accumulo-site.xml");
        if (xmlConfig == null) {
            xmlConfig = new Configuration(false);

            if (SiteConfiguration.class.getClassLoader().getResource(configFile) == null)
                log.warn(configFile + " not found on classpath", new Throwable());
            else
                xmlConfig.addResource(configFile);
        }
        return xmlConfig;
    }

    @Override
    public String get(Property property) {
        String key = property.getKey();

        // If the property is sensitive, see if CredentialProvider was configured.
        if (property.isSensitive()) {
            Configuration hadoopConf = getHadoopConfiguration();
            if (null != hadoopConf) {
                // Try to find the sensitive value from the CredentialProvider
                try {
                    char[] value = CredentialProviderFactoryShim.getValueFromCredentialProvider(hadoopConf, key);
                    if (null != value) {
                        return new String(value);
                    }
                } catch (IOException e) {
                    log.warn("Failed to extract sensitive property (" + key
                            + ") from Hadoop CredentialProvider, falling back to accumulo-site.xml", e);
                }
            }
        }

        /* Check the available-on-load configs and fall-back to the possibly-update Configuration object. */
        String value = staticConfigs.containsKey(key) ? staticConfigs.get(key) : getXmlConfig().get(key);

        if (value == null || !property.getType().isValidFormat(value)) {
            if (value != null)
                log.error("Using default value for " + key + " due to improperly formatted " + property.getType()
                        + ": " + value);
            value = parent.get(property);
        }
        return value;
    }

    @Override
    public void getProperties(Map<String, String> props, Predicate<String> filter) {
        parent.getProperties(props, filter);

        for (Entry<String, String> entry : getXmlConfig())
            if (filter.test(entry.getKey()))
                props.put(entry.getKey(), entry.getValue());

        // CredentialProvider should take precedence over site
        Configuration hadoopConf = getHadoopConfiguration();
        if (null != hadoopConf) {
            try {
                for (String key : CredentialProviderFactoryShim.getKeys(hadoopConf)) {
                    if (!Property.isValidPropertyKey(key) || !Property.isSensitive(key)) {
                        continue;
                    }

                    if (filter.test(key)) {
                        char[] value = CredentialProviderFactoryShim.getValueFromCredentialProvider(hadoopConf,
                                key);
                        if (null != value) {
                            props.put(key, new String(value));
                        }
                    }
                }
            } catch (IOException e) {
                log.warn(
                        "Failed to extract sensitive properties from Hadoop CredentialProvider, falling back to accumulo-site.xml",
                        e);
            }
        }
    }

    protected Configuration getHadoopConfiguration() {
        String credProviderPathsKey = Property.GENERAL_SECURITY_CREDENTIAL_PROVIDER_PATHS.getKey();
        String credProviderPathsValue = getXmlConfig().get(credProviderPathsKey);

        if (null != credProviderPathsValue) {
            // We have configuration for a CredentialProvider
            // Try to pull the sensitive password from there
            Configuration conf = new Configuration(CachedConfiguration.getInstance());
            conf.set(CredentialProviderFactoryShim.CREDENTIAL_PROVIDER_PATH, credProviderPathsValue);
            return conf;
        }

        return null;
    }

    /**
     * Clears the configuration properties in this configuration (but not the parent). This method supports testing and should not be called.
     */
    synchronized public static void clearInstance() {
        instance = null;
    }

    /**
     * Sets a property. This method supports testing and should not be called.
     *
     * @param property
     *          property to set
     * @param value
     *          property value
     */
    public void set(Property property, String value) {
        set(property.getKey(), value);
    }

    /**
     * Sets a property. This method supports testing and should not be called.
     *
     * @param key
     *          key of property to set
     * @param value
     *          property value
     */
    public void set(String key, String value) {
        getXmlConfig().set(key, value);
    }
}