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

com.thinkaurelius.titan.hadoop.config.HadoopConfiguration Maven / Gradle / Ivy

package com.thinkaurelius.titan.hadoop.config;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Predicate;
import org.apache.hadoop.conf.Configuration;
import org.elasticsearch.common.collect.Iterators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.thinkaurelius.titan.core.attribute.Duration;
import com.thinkaurelius.titan.diskstorage.configuration.WriteConfiguration;
import com.thinkaurelius.titan.diskstorage.util.time.Durations;
import com.thinkaurelius.titan.diskstorage.util.time.StandardDuration;

import javax.annotation.Nullable;

public class HadoopConfiguration implements WriteConfiguration {

    private static final Logger log =
            LoggerFactory.getLogger(HadoopConfiguration.class);

    private final Configuration config;
    private final String prefix;

    public HadoopConfiguration(Configuration config) {
        this(config, null);
    }

    public HadoopConfiguration(Configuration config, String prefix) {
        this.config = config;
        this.prefix = prefix;
    }

    @Override
    public  O get(String key, Class datatype) {

        final String internalKey = getInternalKey(key);

        if (null == config.get(internalKey))
            return null;

        if (datatype.isArray()) {
            Preconditions.checkArgument(datatype.getComponentType()==String.class,"Only string arrays are supported: %s",datatype);
            return (O)config.getStrings(internalKey);
        } else if (Number.class.isAssignableFrom(datatype)) {
            String s = config.get(internalKey);
            return constructFromStringArgument(datatype, s);
        } else if (datatype==String.class) {
            return (O)config.get(internalKey);
        } else if (datatype==Boolean.class) {
            return (O)Boolean.valueOf(config.get(internalKey));
        } else if (datatype.isEnum()) {
            O[] constants = datatype.getEnumConstants();
            Preconditions.checkState(null != constants && 0 < constants.length, "Zero-length or undefined enum");
            String estr = config.get(internalKey);
            for (O c : constants)
                if (c.toString().equals(estr))
                    return c;
            throw new IllegalArgumentException("No match for string \"" + estr + "\" in enum " + datatype);
        } else if (datatype==Object.class) {
            // Return String when an Object is requested
            // Object.class must be supported for the sake of AbstractConfiguration's getSubset impl
            return (O)config.get(internalKey);
        } else if (Duration.class.isAssignableFrom(datatype)) {
            // This is a conceptual leak; the config layer should ideally only handle standard library types
            String s = config.get(internalKey);
            String[] comps = s.split("\\s");
            TimeUnit unit = null;
            if (comps.length == 1) {
                //By default, times are in milli seconds
                unit = TimeUnit.MILLISECONDS;
            } else if (comps.length == 2) {
                unit = Durations.parse(comps[1]);
            } else {
                throw new IllegalArgumentException("Cannot parse time duration from: " + s);
            }
            return (O) new StandardDuration(Long.valueOf(comps[0]), unit);
        } else throw new IllegalArgumentException("Unsupported data type: " + datatype);
    }

    @Override
    public Iterable getKeys(final String userPrefix) {
        /*
         * Is there a way to iterate over just the keys of a Hadoop Configuration?
         * Iterating over Map.Entry is needlessly wasteful since we don't need the values.
         */
        Iterable internalKeys = Iterables.transform(config, new Function, String>() {
            @Override
            public String apply(Entry internalEntry) {
                return internalEntry.getKey();
            }
        });

        Iterable prefixedKeys = Iterables.filter(internalKeys, new Predicate() {
            @Override
            public boolean apply(@Nullable String internalKey) {
                String k = internalKey;
                if (null != prefix) {
                    if (k.startsWith(prefix)) {
                        k = getUserKey(k);
                    } else {
                        return false; // does not have the prefix
                    }
                }
                return k.startsWith(userPrefix);
            }
        });

        return Iterables.transform(prefixedKeys, new Function() {

            @Nullable
            @Override
            public String apply(@Nullable String internalKey) {
                String userKey = getUserKey(internalKey);
                Preconditions.checkState(userKey.startsWith(userPrefix));
                return userKey;
            }
        });
    }

    @Override
    public void close() {
        // nothing to do
    }

    @Override
    public  void set(String key, O value) {

        final String internalKey = getInternalKey(key);

        Class datatype = value.getClass();

        if (datatype.isArray()) {
            Preconditions.checkArgument(datatype.getComponentType()==String.class,"Only string arrays are supported: %s",datatype);
            config.setStrings(internalKey, (String[])value);
        } else if (Number.class.isAssignableFrom(datatype)) {
            config.set(internalKey, value.toString());
        } else if (datatype==String.class) {
            config.set(internalKey, value.toString());
        } else if (datatype==Boolean.class) {
            config.setBoolean(internalKey, (Boolean)value);
        } else if (datatype.isEnum()) {
            config.set(internalKey, value.toString());
        } else if (datatype==Object.class) {
            config.set(internalKey, value.toString());
        } else if (Duration.class.isAssignableFrom(datatype)) {
            // This is a conceptual leak; the config layer should ideally only handle standard library types
            String millis = String.valueOf(((Duration)value).getLength(TimeUnit.MILLISECONDS));
            config.set(internalKey, millis);
        } else throw new IllegalArgumentException("Unsupported data type: " + datatype);
    }

    @Override
    public void remove(String key) {
        config.unset(getInternalKey(key));
    }

    @Override
    public WriteConfiguration copy() {
        return new HadoopConfiguration(new Configuration(config), prefix);
    }

    private  O constructFromStringArgument(Class datatype, String arg) {
        try {
            Constructor ctor = datatype.getConstructor(String.class);
            return ctor.newInstance(arg);
        // ReflectiveOperationException is narrower and more appropriate than Exception, but only @since 1.7
        //} catch (ReflectiveOperationException e) {
        } catch (Exception e) {
            log.error("Failed to parse configuration string \"{}\" into type {} due to the following reflection exception", arg, datatype, e);
            throw new RuntimeException(e);
        }
    }

    private String getInternalKey(String userKey) {
        return null == prefix ? userKey : prefix + userKey;
    }

    private String getUserKey(String internalKey) {
        String k = internalKey;

        if (null != prefix) {
            Preconditions.checkState(k.startsWith(prefix), "key %s does not start with prefix %s", internalKey, prefix);
            Preconditions.checkState(internalKey.length() > prefix.length());
            k = internalKey.substring(prefix.length());
        }

        return k;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy