org.apache.accumulo.core.client.ClientConfiguration.java Source code

Java tutorial

Introduction

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

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

import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.conf.PropertyType;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Contains a list of property keys recognized by the Accumulo client and convenience methods for setting them.
 *
 * @since 1.6.0
 */
public class ClientConfiguration extends CompositeConfiguration {
    private static final Logger log = LoggerFactory.getLogger(ClientConfiguration.class);

    public static final String USER_ACCUMULO_DIR_NAME = ".accumulo";
    public static final String USER_CONF_FILENAME = "config";
    public static final String GLOBAL_CONF_FILENAME = "client.conf";

    public enum ClientProperty {
        // SSL
        RPC_SSL_TRUSTSTORE_PATH(Property.RPC_SSL_TRUSTSTORE_PATH), RPC_SSL_TRUSTSTORE_PASSWORD(
                Property.RPC_SSL_TRUSTSTORE_PASSWORD), RPC_SSL_TRUSTSTORE_TYPE(
                        Property.RPC_SSL_TRUSTSTORE_TYPE), RPC_SSL_KEYSTORE_PATH(
                                Property.RPC_SSL_KEYSTORE_PATH), RPC_SSL_KEYSTORE_PASSWORD(
                                        Property.RPC_SSL_KEYSTORE_PASSWORD), RPC_SSL_KEYSTORE_TYPE(
                                                Property.RPC_SSL_KEYSTORE_TYPE), RPC_USE_JSSE(
                                                        Property.RPC_USE_JSSE), GENERAL_SECURITY_CREDENTIAL_PROVIDER_PATHS(
                                                                Property.GENERAL_SECURITY_CREDENTIAL_PROVIDER_PATHS), INSTANCE_RPC_SSL_CLIENT_AUTH(
                                                                        Property.INSTANCE_RPC_SSL_CLIENT_AUTH), INSTANCE_RPC_SSL_ENABLED(
                                                                                Property.INSTANCE_RPC_SSL_ENABLED),

        // ZooKeeper
        INSTANCE_ZK_HOST(Property.INSTANCE_ZK_HOST), INSTANCE_ZK_TIMEOUT(Property.INSTANCE_ZK_TIMEOUT),

        // Instance information
        INSTANCE_NAME("instance.name", null, PropertyType.STRING,
                "Name of Accumulo instance to connect to"), INSTANCE_ID("instance.id", null, PropertyType.STRING,
                        "UUID of Accumulo instance to connect to"),

        // Tracing
        TRACE_SPAN_RECEIVERS(Property.TRACE_SPAN_RECEIVERS), TRACE_SPAN_RECEIVER_PREFIX(
                Property.TRACE_SPAN_RECEIVER_PREFIX), TRACE_ZK_PATH(Property.TRACE_ZK_PATH),

        // SASL / GSSAPI(Kerberos)
        /**
         * @since 1.7.0
         */
        INSTANCE_RPC_SASL_ENABLED(Property.INSTANCE_RPC_SASL_ENABLED),
        /**
         * @since 1.7.0
         */
        RPC_SASL_QOP(Property.RPC_SASL_QOP),
        /**
         * @since 1.7.0
         */
        KERBEROS_SERVER_PRIMARY("kerberos.server.primary", "accumulo", PropertyType.STRING,
                "The first component of the Kerberos principal, the 'primary', that Accumulo servers use to login");

        private String key;
        private String defaultValue;
        private PropertyType type;
        private String description;

        private Property accumuloProperty = null;

        private ClientProperty(Property prop) {
            this(prop.getKey(), prop.getDefaultValue(), prop.getType(), prop.getDescription());
            accumuloProperty = prop;
        }

        private ClientProperty(String key, String defaultValue, PropertyType type, String description) {
            this.key = key;
            this.defaultValue = defaultValue;
            this.type = type;
            this.description = description;
        }

        public String getKey() {
            return key;
        }

        public String getDefaultValue() {
            return defaultValue;
        }

        /**
         * @deprecated since 1.7.0 This method returns a type that is not part of the public API and not guaranteed to be stable.
         */
        @Deprecated
        public PropertyType getType() {
            return type;
        }

        public String getDescription() {
            return description;
        }

        /**
         * @deprecated since 1.7.0 This method returns a type that is not part of the public API and not guaranteed to be stable.
         */
        @Deprecated
        public Property getAccumuloProperty() {
            return accumuloProperty;
        }

        public static ClientProperty getPropertyByKey(String key) {
            for (ClientProperty prop : ClientProperty.values())
                if (prop.getKey().equals(key))
                    return prop;
            return null;
        }
    };

    public ClientConfiguration(String configFile) throws ConfigurationException {
        this(new PropertiesConfiguration(), configFile);
    }

    private ClientConfiguration(PropertiesConfiguration propertiesConfiguration, String configFile)
            throws ConfigurationException {
        super(propertiesConfiguration);
        // Don't do list interpolation
        this.setListDelimiter('\0');
        propertiesConfiguration.setListDelimiter('\0');
        propertiesConfiguration.load(configFile);
    }

    public ClientConfiguration(File configFile) throws ConfigurationException {
        this(new PropertiesConfiguration(), configFile);
    }

    private ClientConfiguration(PropertiesConfiguration propertiesConfiguration, File configFile)
            throws ConfigurationException {
        super(propertiesConfiguration);
        // Don't do list interpolation
        this.setListDelimiter('\0');
        propertiesConfiguration.setListDelimiter('\0');
        propertiesConfiguration.load(configFile);
    }

    public ClientConfiguration(List<? extends Configuration> configs) {
        super(configs);
        // Don't do list interpolation
        this.setListDelimiter('\0');
        for (Configuration c : configs) {
            if (c instanceof AbstractConfiguration) {
                AbstractConfiguration abstractConfiguration = (AbstractConfiguration) c;
                if (!abstractConfiguration.isDelimiterParsingDisabled()
                        && abstractConfiguration.getListDelimiter() != '\0') {
                    log.warn(
                            "Client configuration constructed with a Configuration that did not have list delimiter disabled or overridden, multi-valued config "
                                    + "properties may be unavailable");
                    abstractConfiguration.setListDelimiter('\0');
                }
            }
        }
    }

    /**
     * Iterates through the Configuration objects, populating this object.
     *
     * @see PropertiesConfiguration
     * @see #loadDefault()
     */

    public ClientConfiguration(Configuration... configs) {
        this(Arrays.asList(configs));
    }

    /**
     * Attempts to load a configuration file from the system. Uses the "ACCUMULO_CLIENT_CONF_PATH" environment variable, split on File.pathSeparator, for a list
     * of target files. If not set, uses the following in this order- ~/.accumulo/config $ACCUMULO_CONF_DIR/client.conf -OR- $ACCUMULO_HOME/conf/client.conf
     * (depending on whether $ACCUMULO_CONF_DIR is set) /etc/accumulo/client.conf
     *
     * A client configuration will then be read from each location using PropertiesConfiguration to construct a configuration. That means the latest item will be
     * the one in the configuration.
     *
     * @see PropertiesConfiguration
     * @see File#pathSeparator
     */
    public static ClientConfiguration loadDefault() {
        return loadFromSearchPath(getDefaultSearchPath());
    }

    private static ClientConfiguration loadFromSearchPath(List<String> paths) {
        try {
            List<Configuration> configs = new LinkedList<Configuration>();
            for (String path : paths) {
                File conf = new File(path);
                if (conf.isFile() && conf.canRead()) {
                    configs.add(new ClientConfiguration(conf));
                }
            }
            // We couldn't find the client configuration anywhere
            if (configs.isEmpty()) {
                log.warn("Found no client.conf in default paths. Using default client configuration values.");
            }
            return new ClientConfiguration(configs);
        } catch (ConfigurationException e) {
            throw new IllegalStateException("Error loading client configuration", e);
        }
    }

    public static ClientConfiguration deserialize(String serializedConfig) {
        PropertiesConfiguration propConfig = new PropertiesConfiguration();
        propConfig.setListDelimiter('\0');
        try {
            propConfig.load(new StringReader(serializedConfig));
        } catch (ConfigurationException e) {
            throw new IllegalArgumentException("Error deserializing client configuration: " + serializedConfig, e);
        }
        return new ClientConfiguration(propConfig);
    }

    /**
     * Muck the value of {@code clientConfPath} if it points to a directory by appending {@code client.conf} to the end of the file path. This is a no-op if the
     * value is not a directory on the filesystem.
     *
     * @param clientConfPath
     *          The value of ACCUMULO_CLIENT_CONF_PATH.
     */
    static String getClientConfPath(String clientConfPath) {
        if (null == clientConfPath) {
            return null;
        }
        File filePath = new File(clientConfPath);
        // If clientConfPath is a directory, tack on the default client.conf file name.
        if (filePath.exists() && filePath.isDirectory()) {
            return new File(filePath, "client.conf").toString();
        }
        return clientConfPath;
    }

    private static List<String> getDefaultSearchPath() {
        String clientConfSearchPath = getClientConfPath(System.getenv("ACCUMULO_CLIENT_CONF_PATH"));
        List<String> clientConfPaths;
        if (clientConfSearchPath != null) {
            clientConfPaths = Arrays.asList(clientConfSearchPath.split(File.pathSeparator));
        } else {
            // if $ACCUMULO_CLIENT_CONF_PATH env isn't set, priority from top to bottom is:
            // ~/.accumulo/config
            // $ACCUMULO_CONF_DIR/client.conf -OR- $ACCUMULO_HOME/conf/client.conf (depending on whether $ACCUMULO_CONF_DIR is set)
            // /etc/accumulo/client.conf
            clientConfPaths = new LinkedList<String>();
            clientConfPaths.add(System.getProperty("user.home") + File.separator + USER_ACCUMULO_DIR_NAME
                    + File.separator + USER_CONF_FILENAME);
            if (System.getenv("ACCUMULO_CONF_DIR") != null) {
                clientConfPaths.add(System.getenv("ACCUMULO_CONF_DIR") + File.separator + GLOBAL_CONF_FILENAME);
            } else if (System.getenv("ACCUMULO_HOME") != null) {
                clientConfPaths.add(System.getenv("ACCUMULO_HOME") + File.separator + "conf" + File.separator
                        + GLOBAL_CONF_FILENAME);
            }
            clientConfPaths.add("/etc/accumulo/" + GLOBAL_CONF_FILENAME);
            clientConfPaths.add("/etc/accumulo/conf/" + GLOBAL_CONF_FILENAME);
        }
        return clientConfPaths;
    }

    public String serialize() {
        PropertiesConfiguration propConfig = new PropertiesConfiguration();
        propConfig.copy(this);
        StringWriter writer = new StringWriter();
        try {
            propConfig.save(writer);
        } catch (ConfigurationException e) {
            // this should never happen
            throw new IllegalStateException(e);
        }
        return writer.toString();
    }

    /**
     * Returns the value for prop, the default value if not present.
     *
     */
    public String get(ClientProperty prop) {
        if (this.containsKey(prop.getKey()))
            return this.getString(prop.getKey());
        else
            return prop.getDefaultValue();
    }

    private void checkType(ClientProperty property, PropertyType type) {
        if (!property.getType().equals(type)) {
            String msg = "Configuration method intended for type " + type + " called with a " + property.getType()
                    + " argument (" + property.getKey() + ")";
            throw new IllegalArgumentException(msg);
        }
    }

    /**
     * Gets all properties under the given prefix in this configuration.
     *
     * @param property
     *          prefix property, must be of type PropertyType.PREFIX
     * @return a map of property keys to values
     * @throws IllegalArgumentException
     *           if property is not a prefix
     */
    public Map<String, String> getAllPropertiesWithPrefix(ClientProperty property) {
        checkType(property, PropertyType.PREFIX);

        Map<String, String> propMap = new HashMap<>();
        String prefix = property.getKey();
        if (prefix.endsWith(".")) {
            prefix = prefix.substring(0, prefix.length() - 1);
        }
        Iterator<?> iter = this.getKeys(prefix);
        while (iter.hasNext()) {
            String p = (String) iter.next();
            propMap.put(p, getString(p));
        }
        return propMap;
    }

    /**
     * Sets the value of property to value
     *
     */
    public void setProperty(ClientProperty prop, String value) {
        this.setProperty(prop.getKey(), value);
    }

    /**
     * Same as {@link #setProperty(ClientProperty, String)} but returns the ClientConfiguration for chaining purposes
     */
    public ClientConfiguration with(ClientProperty prop, String value) {
        this.setProperty(prop.getKey(), value);
        return this;
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_NAME
     *
     */
    public ClientConfiguration withInstance(String instanceName) {
        checkArgument(instanceName != null, "instanceName is null");
        return with(ClientProperty.INSTANCE_NAME, instanceName);
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_ID
     *
     */
    public ClientConfiguration withInstance(UUID instanceId) {
        checkArgument(instanceId != null, "instanceId is null");
        return with(ClientProperty.INSTANCE_ID, instanceId.toString());
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_ZK_HOST
     *
     */
    public ClientConfiguration withZkHosts(String zooKeepers) {
        checkArgument(zooKeepers != null, "zooKeepers is null");
        return with(ClientProperty.INSTANCE_ZK_HOST, zooKeepers);
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_ZK_TIMEOUT
     *
     */
    public ClientConfiguration withZkTimeout(int timeout) {
        return with(ClientProperty.INSTANCE_ZK_TIMEOUT, String.valueOf(timeout));
    }

    /**
     * Same as {@link #withSsl(boolean, boolean)} with useJsseConfig set to false
     *
     */
    public ClientConfiguration withSsl(boolean sslEnabled) {
        return withSsl(sslEnabled, false);
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_RPC_SSL_ENABLED and ClientProperty.RPC_USE_JSSE
     *
     */
    public ClientConfiguration withSsl(boolean sslEnabled, boolean useJsseConfig) {
        return with(ClientProperty.INSTANCE_RPC_SSL_ENABLED, String.valueOf(sslEnabled))
                .with(ClientProperty.RPC_USE_JSSE, String.valueOf(useJsseConfig));
    }

    /**
     * Same as {@link #withTruststore(String)} with password null and type null
     *
     */
    public ClientConfiguration withTruststore(String path) {
        return withTruststore(path, null, null);
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.RPC_SSL_TRUSTORE_PATH, ClientProperty.RPC_SSL_TRUSTORE_PASSWORD, and
     * ClientProperty.RPC_SSL_TRUSTORE_TYPE
     *
     */
    public ClientConfiguration withTruststore(String path, String password, String type) {
        checkArgument(path != null, "path is null");
        setProperty(ClientProperty.RPC_SSL_TRUSTSTORE_PATH, path);
        if (password != null)
            setProperty(ClientProperty.RPC_SSL_TRUSTSTORE_PASSWORD, password);
        if (type != null)
            setProperty(ClientProperty.RPC_SSL_TRUSTSTORE_TYPE, type);
        return this;
    }

    /**
     * Same as {@link #withKeystore(String, String, String)} with password null and type null
     *
     */
    public ClientConfiguration withKeystore(String path) {
        return withKeystore(path, null, null);
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_RPC_SSL_CLIENT_AUTH, ClientProperty.RPC_SSL_KEYSTORE_PATH,
     * ClientProperty.RPC_SSL_KEYSTORE_PASSWORD, and ClientProperty.RPC_SSL_KEYSTORE_TYPE
     *
     */
    public ClientConfiguration withKeystore(String path, String password, String type) {
        checkArgument(path != null, "path is null");
        setProperty(ClientProperty.INSTANCE_RPC_SSL_CLIENT_AUTH, "true");
        setProperty(ClientProperty.RPC_SSL_KEYSTORE_PATH, path);
        if (password != null)
            setProperty(ClientProperty.RPC_SSL_KEYSTORE_PASSWORD, password);
        if (type != null)
            setProperty(ClientProperty.RPC_SSL_KEYSTORE_TYPE, type);
        return this;
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_RPC_SASL_ENABLED.
     *
     * @since 1.7.0
     */
    public ClientConfiguration withSasl(boolean saslEnabled) {
        return with(ClientProperty.INSTANCE_RPC_SASL_ENABLED, String.valueOf(saslEnabled));
    }

    /**
     * Same as {@link #with(ClientProperty, String)} for ClientProperty.INSTANCE_RPC_SASL_ENABLED and ClientProperty.GENERAL_KERBEROS_PRINCIPAL.
     *
     * @param saslEnabled
     *          Should SASL(kerberos) be enabled
     * @param kerberosServerPrimary
     *          The 'primary' component of the Kerberos principal Accumulo servers use to login (e.g. 'accumulo' in 'accumulo/_HOST@REALM')
     * @since 1.7.0
     */
    public ClientConfiguration withSasl(boolean saslEnabled, String kerberosServerPrimary) {
        return withSasl(saslEnabled).with(ClientProperty.KERBEROS_SERVER_PRIMARY, kerberosServerPrimary);
    }
}