com.tngtech.configbuilder.ConfigBuilder Maven / Gradle / Ivy
Show all versions of config-builder Show documentation
package com.tngtech.configbuilder;
import com.tngtech.configbuilder.annotation.configuration.LoadingOrder;
import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.ErrorMessageFile;
import com.tngtech.configbuilder.configuration.BuilderConfiguration;
import com.tngtech.configbuilder.configuration.ErrorMessageSetup;
import com.tngtech.configbuilder.util.ConfigBuilderFactory;
import com.tngtech.configbuilder.util.*;
import com.tngtech.propertyloader.PropertyLoader;
import com.tngtech.propertyloader.impl.DefaultPropertyFilterContainer;
import com.tngtech.propertyloader.impl.DefaultPropertyLocationContainer;
import com.tngtech.propertyloader.impl.DefaultPropertySuffixContainer;
import com.tngtech.propertyloader.impl.interfaces.PropertyLoaderFilter;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
/**
* Builds a config object.
* ConfigBuilder instantiates a class and sets fields of the instance by parsing annotations and
* loading values from properties files or the command line. It validates the instance by parsing JSR303 constraint annotations.
*
* Fields of the config class can have the following annotations:
* {@link com.tngtech.configbuilder.annotation.valueextractor.DefaultValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.PropertyValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.SystemPropertyValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.EnvironmentVariableValue}
* {@link LoadingOrder}
*
* Properties files are loaded with a PropertyLoader using its default config. In order to change settings for the PropertyLoader, the config class may be annotated with
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles}
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations}
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes}
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension}
*
* To specify a global order for parsing {@link com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorAnnotation} annotations, annotate the class with
* {@link LoadingOrder}
*
* To specify your own error messages file (which is loaded by the PropertyLoader with the same settings as other the properties files), annotate the class with
* {@link ErrorMessageFile}
*
* @param The type of the config class which shall be instantiated.
* @author Matthias Bollwein
* @version 0.1-SNAPSHOT
*/
public class ConfigBuilder {
private final static Logger LOGGER = LoggerFactory.getLogger(CommandLineHelper.class);
public static final Object AT_CONTEXT_CLASS_PATH = new Object();
private final BuilderConfiguration builderConfiguration;
private final CommandLineHelper commandLineHelper;
private final FieldSetter fieldSetter;
private final ConfigValidator configValidator;
private final ErrorMessageSetup errorMessageSetup;
private final ConstructionHelper constructionHelper;
private Class configClass;
private Options commandLineOptions;
private PropertyLoader propertyLoader;
private Properties additionalProperties;
private String[] commandLineArgs = {};
protected ConfigBuilder(Class configClass, ConfigBuilderFactory configBuilderFactory) {
configBuilderFactory.initialize();
this.configClass = configClass;
this.builderConfiguration = configBuilderFactory.getInstance(BuilderConfiguration.class);
this.commandLineHelper = configBuilderFactory.getInstance(CommandLineHelper.class);
this.configValidator = configBuilderFactory.getInstance(ConfigValidator.class);
this.fieldSetter = configBuilderFactory.getInstance(FieldSetter.class);
this.errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class);
this.constructionHelper = configBuilderFactory.getInstance(ConstructionHelper.class);
this.additionalProperties = configBuilderFactory.createInstance(Properties.class);
propertyLoader = configBuilderFactory.getInstance(PropertyLoaderConfigurator.class).configurePropertyLoader(configClass);
commandLineOptions = commandLineHelper.getOptions(configClass);
}
/**
* @param configClass The config class of which an instance shall be built.
*/
public ConfigBuilder(Class configClass) {
this(configClass, new ConfigBuilderFactory());
}
/**
* Sets the command line arguments that the ConfigBuilder uses in order to parse fields annotated with @CommandLineValue
.
* Command line arguments must match the options specified in the @CommandLineValue
annotations.
*
* @param args the command line arguments
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withCommandLineArgs(String[] args) {
this.commandLineArgs = args;
return this;
}
/**
* Imports the values from the given object according to the field names in the annotations
* @param importedConfiguration
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withImportedConfiguration(Object importedConfiguration) {
builderConfiguration.setImportedConfiguration(importedConfiguration);
return this;
}
/**
* Configures the Config Builder to load given properties files instead of those specified in the config class.
*
* @param baseNames
* @return the instance of ConfigBuilder
*/
public ConfigBuilder overridePropertiesFiles(List baseNames) {
propertyLoader.withBaseNames(baseNames);
return this;
}
/**
* Provide additional properties which will overwrite the properties retrieved by the property loader
*
* @param properties to be added to the properties already present (starting from the result of the property loader)
* @return the instance of ConfigBuilder
*/
public ConfigBuilder addProperties(Properties properties) {
additionalProperties.putAll(properties);
return this;
}
/**
* set the extension to search for property files
* @param propertyExtension property file name extension to use
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertyExtension(String propertyExtension) {
propertyLoader.withExtension(propertyExtension);
return this;
}
/**
* set property suffix to b
* @param propertySuffix property file name suffix
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertySuffix(String propertySuffix) {
return withPropertySuffixes(propertySuffix);
}
/**
* replace list of possible property suffixes by given elements
* @param suffixArray one or more property file name suffix
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertySuffixes(String ... suffixArray) {
final DefaultPropertySuffixContainer suffixes = propertyLoader.getSuffixes();
suffixes.clear();
suffixes.addSuffixList(Arrays.asList(suffixArray));
return this;
}
/**
* add more property file suffixes to the list of possible property suffixes
* @param suffixArray one or more property file name suffix
* @return the instance of ConfigBuilder
*/
public ConfigBuilder addPropertySuffixes(String... suffixArray) {
propertyLoader.getSuffixes().addSuffixList(Arrays.asList(suffixArray));
return this;
}
/**
* set file name of property file to read
* @param fileName property file name
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertiesFile(String fileName) {
return withPropertiesFiles(fileName);
}
/**
* set file names of property files to read
* @param fileNames one or more property file names
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertiesFiles(String ... fileNames) {
propertyLoader.withBaseNames(Arrays.asList(fileNames));
return this;
}
/**
* set property locations
* @param propertyLocations lists of property locations which can be
* Strings: use as Directory
* Classes: use as Class Resource Location
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertyLocations(Object ... propertyLocations) {
final DefaultPropertyLocationContainer locations = propertyLoader.getLocations();
locations.clear();
for (Object propertyLocation : propertyLocations) {
if (propertyLocation instanceof String) {
locations.atDirectory((String)propertyLocation);
} else if (propertyLocation instanceof Class) {
locations.atRelativeToClass((Class)propertyLocation);
} else if (propertyLocation == AT_CONTEXT_CLASS_PATH) {
locations.atContextClassPath();
} else {
LOGGER.warn("unhandled property location '{}'", propertyLocation);
}
}
return this;
}
/**
* set property filters in use
* @param propertyFilters property filters which should be applied after loading properties
* @return the instance of ConfigBuilder
*/
public ConfigBuilder withPropertyFilters(Class extends PropertyLoaderFilter> ... propertyFilters) {
final DefaultPropertyFilterContainer filterContainer = propertyLoader.getFilters();
final List filters = filterContainer.getFilters();
filters.clear();
for (Class extends PropertyLoaderFilter> propertyFilter : propertyFilters) {
try {
filters.add(propertyFilter.newInstance());
} catch (InstantiationException e) {
LOGGER.error("could not create filter '{}'", propertyFilter.getSimpleName(), e);
} catch (IllegalAccessException e) {
LOGGER.error("could not create filter '{}'", propertyFilter.getSimpleName(), e);
}
}
return this;
}
/**
* Prints a help message for all command line options that are configured in the config class.
*/
public void printCommandLineHelp() {
HelpFormatter formatter = new HelpFormatter();
formatter.setSyntaxPrefix("Command Line Options for class " + configClass.getSimpleName() + ":");
formatter.printHelp(" ", commandLineOptions);
}
/**
* Gets an instance of the config, sets the fields and validates them.
* The method sets up the configuration by loading properties and parsing command line arguments,
* then tries to find a constructor of the config class that matches the arguments passed to it,
* instantiates the config class, sets its fields and validates the instance.
*
* @param objects a vararg of Objects passed to a corresponding constructor of the config class.
* @return An instance of the config class.
*/
public T build(Object... objects) {
initializeErrorMessageSetup(propertyLoader);
setupBuilderConfiguration(propertyLoader);
T instanceOfConfigClass = constructionHelper.getInstance(configClass, objects);
fieldSetter.setFields(instanceOfConfigClass, builderConfiguration);
configValidator.validate(instanceOfConfigClass);
return instanceOfConfigClass;
}
private void setupBuilderConfiguration(PropertyLoader propertyLoader) {
if (configClass.isAnnotationPresent(LoadingOrder.class)) {
builderConfiguration.setAnnotationOrder(configClass.getAnnotation(LoadingOrder.class).value());
}
final Properties properties = propertyLoader.load();
properties.putAll(additionalProperties);
builderConfiguration.setProperties(properties);
builderConfiguration.setCommandLine(commandLineHelper.getCommandLine(configClass, commandLineArgs));
}
private void initializeErrorMessageSetup(PropertyLoader propertyLoader) {
String errorMessageFile = configClass.isAnnotationPresent(ErrorMessageFile.class) ? configClass.getAnnotation(ErrorMessageFile.class).value() : null;
errorMessageSetup.initialize(errorMessageFile, propertyLoader);
}
/**
* Gets an instance of the ConfigBuilder for a given config class
*
* @param clazz config class for which the config builder is instantiated.
* @return ConfigBuilder instance for config class
*/
public static ConfigBuilder on(Class clazz) {
return new ConfigBuilder(clazz);
}
}