org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration.java

Source

// Copyright 2017 JanusGraph Authors
//
// Licensed 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.janusgraph.diskstorage.configuration.backend;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.janusgraph.diskstorage.util.time.Durations;

import org.janusgraph.diskstorage.configuration.ReadConfiguration;
import org.janusgraph.diskstorage.configuration.WriteConfiguration;

import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * {@link ReadConfiguration} wrapper for Apache Configuration
 *
 * @author Matthias Broecheler (me@matthiasb.com)
 */
public class CommonsConfiguration implements WriteConfiguration {

    private final Configuration config;

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

    public CommonsConfiguration() {
        this(new BaseConfiguration());
    }

    public CommonsConfiguration(Configuration config) {
        Preconditions.checkArgument(config != null);
        this.config = config;
    }

    public Configuration getCommonConfiguration() {
        return config;
    }

    @Override
    public <O> O get(String key, Class<O> datatype) {
        if (!config.containsKey(key))
            return null;

        if (datatype.isArray()) {
            Preconditions.checkArgument(datatype.getComponentType() == String.class,
                    "Only string arrays are supported: %s", datatype);
            return (O) config.getStringArray(key);
        } else if (Number.class.isAssignableFrom(datatype)) {
            // A properties file configuration returns Strings even for numeric
            // values small enough to fit inside Integer (e.g. 5000). In-memory
            // configuration impls seem to be able to store and return actual
            // numeric types rather than String
            //
            // We try to handle either case here
            Object o = config.getProperty(key);
            if (datatype.isInstance(o)) {
                return (O) o;
            } else {
                return constructFromStringArgument(datatype, o.toString());
            }
        } else if (datatype == String.class) {
            return (O) config.getString(key);
        } else if (datatype == Boolean.class) {
            return (O) new Boolean(config.getBoolean(key));
        } else if (datatype.isEnum()) {
            Enum[] constants = (Enum[]) datatype.getEnumConstants();
            Preconditions.checkState(null != constants && 0 < constants.length, "Zero-length or undefined enum");

            String estr = config.getProperty(key).toString();
            for (Enum ec : constants)
                if (ec.toString().equals(estr))
                    return (O) ec;
            throw new IllegalArgumentException("No match for string \"" + estr + "\" in enum " + datatype);
        } else if (datatype == Object.class) {
            return (O) config.getProperty(key);
        } else if (Duration.class.isAssignableFrom(datatype)) {
            // This is a conceptual leak; the config layer should ideally only handle standard library types
            Object o = config.getProperty(key);
            if (Duration.class.isInstance(o)) {
                return (O) o;
            } else {
                String[] comps = o.toString().split("\\s");
                TemporalUnit unit = null;
                if (comps.length == 1) {
                    //By default, times are in milli seconds
                    unit = ChronoUnit.MILLIS;
                } else if (comps.length == 2) {
                    unit = Durations.parse(comps[1]);
                } else {
                    throw new IllegalArgumentException("Cannot parse time duration from: " + o.toString());
                }
                return (O) Duration.of(Long.valueOf(comps[0]), unit);
            }
            // Lists are deliberately not supported.  List's generic parameter
            // is subject to erasure and can't be checked at runtime.  Someone
            // could create a ConfigOption<List<Number>>; we would instead return
            // a List<String> like we always do at runtime, and it wouldn't break
            // until the client tried to use the contents of the list.
            //
            // We could theoretically get around this by adding a type token to
            // every declaration of a List-typed ConfigOption, but it's just
            // not worth doing since we only actually use String[] anyway.
            //        } else if (List.class.isAssignableFrom(datatype)) {
            //            return (O) config.getProperty(key);
        } else
            throw new IllegalArgumentException("Unsupported data type: " + datatype);
    }

    private <O> O constructFromStringArgument(Class<O> datatype, String arg) {
        try {
            Constructor<O> 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);
        }
    }

    @Override
    public Iterable<String> getKeys(String prefix) {
        List<String> result = Lists.newArrayList();
        Iterator<String> keys;
        if (StringUtils.isNotBlank(prefix))
            keys = config.getKeys(prefix);
        else
            keys = config.getKeys();
        while (keys.hasNext())
            result.add(keys.next());
        return result;
    }

    @Override
    public void close() {
        //Do nothing
    }

    @Override
    public <O> void set(String key, O value) {
        if (value == null) {
            config.clearProperty(key);
        } else if (Duration.class.isAssignableFrom(value.getClass())) {
            config.setProperty(key, ((Duration) value).toMillis());
        } else {
            config.setProperty(key, value);
        }
    }

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

    @Override
    public WriteConfiguration copy() {
        BaseConfiguration copy = new BaseConfiguration();
        copy.copy(config);
        return new CommonsConfiguration(copy);
    }

}