blob: 2593be611e02ef4af6823085ec3a1fb7bf288a87 [file] [log] [blame]
/*
* Copyright 2017 HugeGraph Authors
*
* 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 com.baidu.hugegraph.config;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.AbstractFileConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.Log;
public class HugeConfig extends PropertiesConfiguration {
private static final Logger LOG = Log.logger(HugeConfig.class);
public HugeConfig(Configuration config) {
if (config == null) {
throw new ConfigException("The config object is null");
}
this.reloadIfNeed(config);
this.setLayoutIfNeeded(config);
Iterator<String> keys = config.getKeys();
while (keys.hasNext()) {
String key = keys.next();
this.addProperty(key, config.getProperty(key));
}
this.checkRequiredOptions();
}
public HugeConfig(String configFile) {
this(loadConfigFile(configFile));
}
private void reloadIfNeed(Configuration conf) {
if (!(conf instanceof AbstractFileConfiguration)) {
if (conf instanceof AbstractConfiguration) {
AbstractConfiguration config = (AbstractConfiguration) conf;
config.setDelimiterParsingDisabled(true);
}
return;
}
AbstractFileConfiguration fileConfig = (AbstractFileConfiguration) conf;
File file = fileConfig.getFile();
if (file != null) {
// May need to use the original file
this.setFile(file);
}
if (!fileConfig.isDelimiterParsingDisabled()) {
/*
* PropertiesConfiguration will parse the containing comma
* config options into list directly, but we want to do
* this work by ourselves, so reload it and parse into `String`
*/
fileConfig.setDelimiterParsingDisabled(true);
try {
fileConfig.refresh();
} catch (ConfigurationException e) {
throw new ConfigException("Unable to load config file: %s",
e, file);
}
}
}
private void setLayoutIfNeeded(Configuration conf) {
if (!(conf instanceof PropertiesConfiguration)) {
return;
}
PropertiesConfiguration propConf = (PropertiesConfiguration) conf;
this.setLayout(propConf.getLayout());
}
private static PropertiesConfiguration loadConfigFile(String path) {
E.checkNotNull(path, "config path");
E.checkArgument(!path.isEmpty(),
"The config path can't be empty");
File file = new File(path);
E.checkArgument(file.exists() && file.isFile() && file.canRead(),
"Need to specify a readable config, but got: %s",
file.toString());
PropertiesConfiguration config = new PropertiesConfiguration();
config.setDelimiterParsingDisabled(true);
try {
config.load(file);
} catch (ConfigurationException e) {
throw new ConfigException("Unable to load config: %s", e, path);
}
return config;
}
@SuppressWarnings("unchecked")
public <T, R> R get(TypedOption<T, R> option) {
Object value = this.getProperty(option.name());
return value != null ? (R) value : option.defaultValue();
}
public Map<String, String> getMap(ConfigListOption<String> option) {
List<String> values = this.get(option);
Map<String, String> result = new HashMap<>();
for (String value : values) {
String[] pair = value.split(":", 2);
E.checkState(pair.length == 2,
"Invalid option format for '%s': %s(expect KEY:VALUE)",
option.name(), value);
result.put(pair[0].trim(), pair[1].trim());
}
return result;
}
@Override
public void addProperty(String key, Object value) {
if (!OptionSpace.containKey(key)) {
LOG.warn("The config option '{}' is redundant, " +
"please ensure it has been registered", key);
} else {
// The input value is String(parsed by PropertiesConfiguration)
value = this.validateOption(key, value);
}
super.addPropertyDirect(key, value);
}
private Object validateOption(String key, Object value) {
E.checkArgument(value instanceof String,
"Invalid value for key '%s': %s", key, value);
TypedOption<?, ?> option = OptionSpace.get(key);
return option.parseConvert((String) value);
}
private void checkRequiredOptions() {
// TODO: Check required options must be contained in this map
}
}