Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.socialbakers.config.AbstractConfiguration Maven / Gradle / Ivy
package com.socialbakers.config;
import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.socialbakers.config.exception.ConfigurationException;
import com.socialbakers.config.exception.DumpException;
import com.socialbakers.config.exception.HelpException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
/**
* @author Robert Fišer
*
*/
public abstract class AbstractConfiguration {
public static ParamValueSeparator PARAM_VALUE_SEPARATOR;
public static boolean ALWAYS_RELOAD;
public static String CONF_DIR_ENV;
public static String DEFAULT_CONF_DIR_ENV = "CONF_DIR";
static final String NAME_PREFIX = "--";
static final String OPTION_PREFIX = "-";
public static final String OPTION_PATTERN = "[a-zA-Z]";
public static final String NAME_PATTERN = "[a-zA-Z0-9]+([_\\.][a-zA-Z0-9]+)*";
private static final String HELP_NAME = NAME_PREFIX + IParamDefinition.HELP;
private static final String HELP_OPTION = OPTION_PREFIX + IParamDefinition.HELP;
private static final String DUMP_NAME = NAME_PREFIX + IParamDefinition.DUMP;
private static final String DUMP_OPTION = OPTION_PREFIX + IParamDefinition.DUMP;
public static String replaceDots(String name) {
return name.replaceAll("\\.", "_");
}
public static void setArgFormatIfItsEmpty(ParamValueSeparator argFormat) {
if (PARAM_VALUE_SEPARATOR == null) {
PARAM_VALUE_SEPARATOR = argFormat;
}
}
public static void setConfDirEnvNameIfItsEmpty(String confDirEnvName) {
if (CONF_DIR_ENV == null) {
CONF_DIR_ENV = confDirEnvName;
}
}
private final Logger logger = LoggerFactory.getLogger(getClass());
private List resources = new ArrayList();
private String[] args = new String[0];
private String helpName = "app-name";
private String helpDescription = "";
private String envFile;
private List confDefs;
private Map properties;
private Map byName = new HashMap();
private Map byEnv = new HashMap();
private Map byOption = new HashMap();
private Map byOrder = new HashMap();
private boolean initLoad;
protected boolean suspendValidation;
/*
* TODO configurable
*/
private String multivalueSeparator = " ";
private Comparator paramOrderComparator = new Comparator() {
@Override
public int compare(IParamDefinition o1, IParamDefinition o2) {
if (o1.getOrder() == null && o2.getOrder() == null) {
return 0;
} else if (o1.getOrder() == null) {
return 1;
} else if (o2.getOrder() == null) {
return -1;
}
return o1.getOrder() - o2.getOrder();
}
};
private static final Set SKIP_ARGS = new HashSet(Arrays.asList(HELP_NAME, HELP_OPTION, DUMP_NAME,
DUMP_OPTION));
public AbstractConfiguration(String[] args) {
this.args = args;
this.confDefs = new ArrayList();
confDefs.addAll(knownParams());
Collections.sort(this.confDefs, paramOrderComparator);
for (IParamDefinition confDef : this.confDefs) {
if (StringUtils.isNotBlank(confDef.getName())) {
byName.put(confDef.getName(), confDef);
}
if (StringUtils.isNotBlank(confDef.getName())) {
byName.put(replaceDots(confDef.getName()), confDef);
}
for (String env : confDef.getEnvs()) {
byEnv.put(env, confDef);
}
if (StringUtils.isNotBlank(confDef.getOption())) {
byOption.put(confDef.getOption(), confDef);
}
if (confDef.getOrder() != null) {
byOrder.put(confDef.getOrder(), confDef);
}
}
}
public void addResource(File file) {
resources.add(file);
}
public void addResource(String filename) {
resources.add(filename);
}
public String getHelpDescription() {
return helpDescription;
}
public String getHelpName() {
return helpName;
}
protected String getEnvFile() {
return envFile;
}
protected Collection knownParams() {
return Collections.emptyList();
}
protected void reload() {
if (!initLoad) {
Envio.loadConfiguration(envFile);
}
boolean alwaysReload = ALWAYS_RELOAD;
ALWAYS_RELOAD = false;
try {
reloadFromFiles();
reloadFromEnvVars();
reloadFromArgs();
validate();
} catch (ConfigurationException e) {
throw e;
} catch (Exception e) {
throw new ConfigurationException(e + "\n" + helpMsg());
} finally {
ALWAYS_RELOAD = alwaysReload;
initLoad = true;
}
}
protected void reloadIfNecessary() {
if (ALWAYS_RELOAD || !initLoad) {
reload();
}
}
protected void setEnvFile(String envFile) {
this.envFile = envFile;
}
protected void setHelpDescription(String helpDescription) {
this.helpDescription = helpDescription;
}
protected void setHelpName(String helpName) {
this.helpName = helpName;
}
protected void validate() {
if (suspendValidation) {
return;
}
StringBuilder msg = new StringBuilder();
boolean valid;
try {
Set notSetRequiredParams = new LinkedHashSet();
for (IParamDefinition confDef : byName.values()) {
if (!confDef.isRequired()) {
continue;
}
PropertyDescriptor descriptor = getProperties().get(confDef);
Object value = descriptor.getReadMethod().invoke(this);
if (value == null) {
notSetRequiredParams.add(confDef);
}
}
valid = notSetRequiredParams.isEmpty();
msg.append("You must pass/set at least ");
msg.append(notSetRequiredParams.size());
msg.append(" parameters:");
msg.append(usage(notSetRequiredParams));
msg.append("\n");
msg.append("All options:");
msg.append(usage(confDefs));
msg.append("\n");
msg.append(dump());
} catch (Exception e) {
throw new ConfigurationException(e);
}
if (!valid) {
throw new ConfigurationException(msg.toString());
}
}
private String dump() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
StringBuilder sb = new StringBuilder();
sb.append("ACTUAL STATE:");
sb.append("\n");
for (IParamDefinition confDef : confDefs) {
PropertyDescriptor descriptor = getProperties().get(confDef);
Object value = descriptor.getReadMethod().invoke(this);
// sb.append(" " + formatOption(confDef, value, false));
sb.append(confDef.getName());
sb.append(":\t");
sb.append(value);
sb.append("\n");
}
return sb.toString();
}
private String formatOption(IParamDefinition confDef, Object value, boolean bracets) {
if (bracets) {
value = String.format("<%s>", value.toString());
}
if (confDef.getOrder() != null) {
return value.toString();
} else if (StringUtils.isNotBlank(confDef.getOption())) {
return OPTION_PREFIX + confDef.getOption() + value;
} else {
return NAME_PREFIX + confDef.getName() + value;
}
}
private File getConfDir() {
String confDirName = System.getenv(CONF_DIR_ENV);
if (confDirName == null) {
confDirName = System.getenv(DEFAULT_CONF_DIR_ENV);
}
if (confDirName == null) {
confDirName = "conf/";
}
return new File(confDirName);
}
private Map getProperties() {
if (properties != null) {
return properties;
}
properties = new HashMap();
for (PropertyDescriptor descriptor : getPropertyDescriptors(this)) {
String name = descriptor.getName();
IParamDefinition confDef = byName.get(name);
properties.put(confDef, descriptor);
}
return properties;
}
private List getResourcesAsStrings() {
List result = new ArrayList();
for (Object resource : resources) {
result.add(resource.toString());
}
return result;
}
private String helpMsg() {
try {
Configuration cfg = new Configuration();
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setClassForTemplateLoading(AbstractConfiguration.class, "templates");
Template helpTemplate = cfg.getTemplate("help.ftl");
Map input = new HashMap();
input.put("name", getHelpName());
input.put("description", getHelpDescription());
input.put("usage", usage(confDefs));
input.put("params", confDefs);
input.put("helpName", HELP_NAME);
input.put("helpOption", HELP_OPTION);
input.put("dumpName", DUMP_NAME);
input.put("dumpOption", DUMP_OPTION);
input.put("namePrefix", NAME_PREFIX);
input.put("optionPrefix", OPTION_PREFIX);
input.put("resources", getResourcesAsStrings());
StringWriter writer = new StringWriter();
try {
helpTemplate.process(input, writer);
return writer.toString();
} finally {
writer.close();
}
} catch (Exception e) {
return e.getMessage();
}
}
private Object instantiateValue(String stringValue, Class> type) {
Object value;
if (type.isAssignableFrom(String.class)) {
value = stringValue;
} else if (Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) {
value = Integer.valueOf(stringValue);
} else if (Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) {
value = Long.valueOf(stringValue);
} else {
throw new ConfigurationException("Unsupported type " + type.getName());
}
return value;
}
private Object instantiateValue(String stringValue, IParamDefinition confDef) throws ClassNotFoundException {
PropertyDescriptor descriptor = getProperties().get(confDef);
Class> type = descriptor.getPropertyType();
if (type.isAssignableFrom(List.class)) {
if (StringUtils.isBlank(stringValue)) {
return Collections.emptyList();
}
String innerTypeName = confDef.getJavaType().replaceFirst("List<", "").replaceFirst(">", "");
if (!innerTypeName.startsWith("java.lang.")) {
innerTypeName = "java.lang." + innerTypeName;
}
type = Class.forName(innerTypeName);
List value = new ArrayList();
for (String sv : stringValue.split(multivalueSeparator)) {
value.add(instantiateValue(sv, type));
}
return value;
}
return instantiateValue(stringValue, type);
}
private boolean isDumpArg(String arg) {
return arg.startsWith(DUMP_NAME) || arg.startsWith(DUMP_OPTION);
}
private boolean isHelpArg(String arg) {
return arg.startsWith(HELP_NAME) || arg.startsWith(HELP_OPTION);
}
private void reloadFromArgs() {
int iPos = 0;
for (int i = 0; i < args.length; i++) {
if (SKIP_ARGS.contains(args[i])) {
continue;
}
String arg = args[i];
IParamDefinition confDef;
String source = arg;
if (PARAM_VALUE_SEPARATOR.matchName(arg)) {
confDef = byName.get(PARAM_VALUE_SEPARATOR.name(arg));
} else if (PARAM_VALUE_SEPARATOR.matchOption(arg)) {
confDef = byOption.get(PARAM_VALUE_SEPARATOR.option(arg));
} else {
confDef = byOrder.get(iPos++);
if (confDef == null) {
continue;
}
setValue(confDef, args[i], ConfigSource.ARG, source);
continue;
}
if (confDef == null) {
throw new IllegalArgumentException("Invalid argument: " + arg);
}
if (PARAM_VALUE_SEPARATOR.getValuePlace() == ParamValueSeparator.ValuePlace.NEXT_ARG
&& (i + 1) >= args.length) {
throw new IllegalArgumentException("Missing value for argument: " + arg);
}
PropertyDescriptor descriptor = getProperties().get(confDef);
if (PARAM_VALUE_SEPARATOR.getValuePlace() == ParamValueSeparator.ValuePlace.NEXT_ARG
&& descriptor.getPropertyType().isAssignableFrom(List.class)) {
String value = "";
while ((i + 1) < args.length && !PARAM_VALUE_SEPARATOR.matchName(args[i + 1])
&& !PARAM_VALUE_SEPARATOR.matchOption(args[i + 1])) {
if (StringUtils.isNotBlank(value)) {
value += " ";
}
value += args[i + 1];
i++;
}
setValue(confDef, value, ConfigSource.ARG, source);
} else {
if (PARAM_VALUE_SEPARATOR.getValuePlace() == ParamValueSeparator.ValuePlace.NEXT_ARG) {
i++;
}
setValue(confDef, PARAM_VALUE_SEPARATOR.getStringValue(args[i]), ConfigSource.ARG, source);
}
}
for (String arg : args) {
Preconditions.checkNotNull(arg);
if (isHelpArg(arg)) {
String msg;
msg = helpMsg();
throw new HelpException(msg);
} else if (isDumpArg(arg)) {
String msg;
suspendValidation = true;
try {
msg = dump();
} catch (Exception e) {
msg = e.getMessage();
}
throw new DumpException(msg);
}
}
}
private void reloadFromEnvVars() {
for (IParamDefinition confDef : byEnv.values()) {
for (String env : confDef.getEnvs()) {
String value = System.getenv(env);
if (value != null) {
setValue(confDef, value, ConfigSource.ENV, env);
break;
}
}
}
}
private void reloadFromFiles() {
SAXBuilder builder = new SAXBuilder();
for (Object resource : resources) {
try {
Document document = null;
File configFile = null;
if (resource instanceof String) {
configFile = new File(getConfDir(), (String) resource);
} else if (resource instanceof File) {
configFile = (File) resource;
}
if (configFile != null) {
if (!configFile.exists()) {
logger.info("Config file '{}' does not exists", configFile.getAbsolutePath());
continue;
}
logger.info("Reading configuration from file '{}':", configFile.getAbsolutePath());
document = builder.build(configFile);
}
// TODO inpustream, url, ...
if (document != null) {
Element rootNode = document.getRootElement();
List> list = rootNode.getChildren("property");
for (int i = 0; i < list.size(); i++) {
Element node = (Element) list.get(i);
String name = node.getChildText("name");
String value = node.getChildText("value");
IParamDefinition confDef = byName.get(name);
setValue(confDef, value, ConfigSource.FILE, configFile.getName());
}
}
} catch (Exception e) {
throw new ConfigurationException(e);
}
}
}
private void setValue(IParamDefinition confDef, String stringValue, ConfigSource sourceType, String source) {
try {
Object value = instantiateValue(stringValue, confDef);
PropertyDescriptor descriptor = getProperties().get(confDef);
Object previous = descriptor.getReadMethod().invoke(this);
if (previous == null || !previous.equals(value)) {
logger.info("Setting value '{}' of property '{}' from source {} '{}'", value, confDef.getName(),
sourceType, source);
}
boolean hold = suspendValidation;
suspendValidation = true;
descriptor.getWriteMethod().invoke(this, value);
suspendValidation = hold;
} catch (Exception e) {
throw new ConfigurationException(e);
}
}
private String usage(Collection params) {
StringBuilder sb = new StringBuilder();
for (IParamDefinition confDef : params) {
sb.append(" ");
if (!confDef.isRequired()) {
sb.append("[");
}
sb.append(formatOption(confDef, confDef.getName(), true));
if (!confDef.isRequired()) {
sb.append("]");
}
}
return sb.toString();
}
private enum ConfigSource {
ARG, ENV, FILE
}
}