// 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.util.system;
import com.google.common.base.Preconditions;
import org.janusgraph.diskstorage.configuration.ConfigElement;
import org.janusgraph.diskstorage.configuration.ConfigOption;
import org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration;
import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
public class ConfigurationLint {
private static final Logger log =
LoggerFactory.getLogger(ConfigurationLint.class);
public static void main(String args[]) throws IOException {
if (1 != args.length) {
System.err.println("Usage: ConfigurationLint janusgraph.properties");
System.err.println(" Reads the supplied config file from disk and checks for unknown options.");
System.exit(1);
}
log.info("Checking " + args[0]);
Status s = validate(args[0]);
if (0 == s.errors) {
log.info(s.toString());
} else {
log.warn(s.toString());
}
System.exit(Math.min(s.errors, 127));
}
public static Status validate(String filename) throws IOException {
Properties p = new Properties();
FileInputStream fis = new FileInputStream(filename);
p.load(fis);
fis.close();
final PropertiesConfiguration apc;
try {
apc = new PropertiesConfiguration(filename);
} catch (ConfigurationException e) {
throw new IOException(e);
}
// new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,
// , BasicConfiguration.Restriction.NONE);
Iterator<String> iter = apc.getKeys();
int totalKeys = 0;
int keysVerified = 0;
while (iter.hasNext()) {
totalKeys++;
String key = iter.next();
String value = apc.getString(key);
try {
ConfigElement.PathIdentifier pid = ConfigElement.parse(GraphDatabaseConfiguration.ROOT_NS, key);
// ConfigElement shouldn't return null; failure here probably relates to janusgraph-core, not the file
Preconditions.checkState(null != pid);
Preconditions.checkState(null != pid.element);
if (!pid.element.isOption()) {
log.warn("Config key {} is a namespace (only options can be keys)", key);
continue;
}
final ConfigOption<?> opt;
try {
opt = (ConfigOption<?>) pid.element;
} catch (RuntimeException re) {
// This shouldn't happen given the preceding check, but catch it anyway
log.warn("Config key {} maps to the element {}, but it could not be cast to an option",
key, pid.element, re);
continue;
}
try {
Object o = new CommonsConfiguration(apc).get(key, opt.getDatatype());
opt.verify(o);
keysVerified++;
} catch (RuntimeException re) {
log.warn("Config key {} is recognized, but its value {} could not be validated",
key, value /*, re*/);
log.debug("Validation exception on {}={} follows", key, value, re);
}
} catch (RuntimeException re) {
log.warn("Unknown config key {}", key);
}
}
return new Status(totalKeys, totalKeys - keysVerified);
}
public static class Status {
private final int total;
private final int errors;
public Status(int total, int errors) {
this.total = total;
this.errors = errors;
}
public int getTotalSettingCount() {
return total;
}
public int getErrorSettingCount() {
return errors;
}
public String toString() {
if (0 == errors) {
return String.format("[ConfigurationLint.Status: OK: %d settings validated]", total);
} else {
return String.format("[ConfigurationLint.Status: WARNING: %d settings failed to validate out of %d total]", errors, total);
}
}
}
}