All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cz.jalasoft.lifeconfig.LifeConfig Maven / Gradle / Ivy

package cz.jalasoft.lifeconfig;

import cz.jalasoft.lifeconfig.converter.TypesafeConverter;
import cz.jalasoft.lifeconfig.converterprovider.*;
import cz.jalasoft.lifeconfig.converter.ConverterRepository;
import cz.jalasoft.lifeconfig.format.ConfigFormat;
import cz.jalasoft.lifeconfig.format.HoconFormat;
import cz.jalasoft.lifeconfig.format.JavaPropertyFormat;
import cz.jalasoft.lifeconfig.format.YamlFormat;
import cz.jalasoft.lifeconfig.keyresolver.PropertyKeyResolver;
import cz.jalasoft.lifeconfig.logger.Logger;
import cz.jalasoft.lifeconfig.logger.LoggerFactory;
import cz.jalasoft.lifeconfig.reader.ConfigReader;
import cz.jalasoft.lifeconfig.reader.ConvertingConfigReader;
import cz.jalasoft.lifeconfig.reader.ReloadingConfigReader;
import cz.jalasoft.lifeconfig.source.ConfigSource;
import cz.jalasoft.lifeconfig.source.FileConfigSource;
import cz.jalasoft.lifeconfig.validation.ConfigInterfaceValidator;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static cz.jalasoft.lifeconfig.util.ArgumentAssertion.mustNotBeNull;
import static cz.jalasoft.lifeconfig.util.ArgumentAssertion.mustNotBeNullOrEmpty;
import static cz.jalasoft.lifeconfig.source.ClassPathConfigSource.*;

import static cz.jalasoft.lifeconfig.keyresolver.PropertyKeyResolvers.*;

/**
 *  A main class of LifeConfig library, that allows specify configuration resource, format etc,
 *  ending with a dynamic proxy providing configuration properties.
 *
 *  @see "https://github.com/jalasoft/LifeConfig"
 *
 * @author Honza Lastovicka
 */
public final class LifeConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(LifeConfig.class);

    public static  LifeConfig pretending(Class type) {
        mustNotBeNull(type, "Configuration interface");

        return new LifeConfig(type);
    }

    //-------------------------------------------------------------------------------------
    //INSTANCE SCOPE
    //-------------------------------------------------------------------------------------

    private ConfigSource source;
    private ConfigFormat format;
    private boolean isLife;

    private final Class type;
    private final ConverterRepository converterRepository;

    private LifeConfig(Class type) {
        this.type = type;
        this.converterRepository = new ConverterRepository();

        LOGGER.debug("-----------------------------");
        LOGGER.debug("Initialization of LifeConfig.");
        LOGGER.debug("-----------------------------");
    }

    /**
     * Registers custom simpleConverter from a string property to a data type.
     *
     * @param converter a simpleConverter from string, must not be null
     * @throws java.lang.IllegalArgumentException if type or simpleConverter is null
     */
    public  LifeConfig addConverter(TypesafeConverter converter) {
        mustNotBeNull(converter, "Converter to be registered must not be null.");

        converterRepository.registerConverter(converter);

        LOGGER.debug("Converter of type " + converter.getClass() + " registered.");
        return this;
    }

    /**
     * Defines a file as a source of the configuration.
     * @param filePath must be a regular file path.
     * @return the same LifeConfig
     * @throws IllegalArgumentException if filePath is null, empty or not a regular file.
     */
    public LifeConfig fromFile(String filePath) {
        mustNotBeNullOrEmpty(filePath, "File path");

        return fromFile(Paths.get(filePath));
    }

    /**
     * Defines a file as a source of the configuration.
     * @param path must be a regular file path.
     * @return the same LifeConfig
     * @throws IllegalArgumentException if filePath is null, or not a regular file.
     */
    public LifeConfig fromFile(Path path) {
        mustNotBeNull(path, "Resource file");

        if (!Files.isRegularFile(path)) {
            throw new IllegalArgumentException("File path '" + path + "' must lead to a regular file.");
        }

        LOGGER.debug("File resource specified: " + path);
        return from(new FileConfigSource(path));
    }

    /**
     * specifies that a file on classpath will be used as a source of configuration.
     * @param resourceName must not be null or empty.
     * @return the same LifeConfig
     * @throws IllegalArgumentException if resourceName is null or empty.
     */
    public LifeConfig fromClasspath(String resourceName) {
        mustNotBeNullOrEmpty(resourceName, "Resource name");

        LOGGER.debug("Classpath resource specified: " + resourceName);
        return from(defaultClassloader(resourceName));
    }

    public LifeConfig fromClasspath(ClassLoader classloader, String resourceName) {
        mustNotBeNull(classloader, "Classloader");
        mustNotBeNullOrEmpty(resourceName, "Resource name");

        LOGGER.debug("Classpath resource specified: " + resourceName);
        return from(classLoader(classloader, resourceName));
    }

    public LifeConfig fromClasspath(Class type, String resourceName) {
        mustNotBeNull(type, "Class");
        mustNotBeNullOrEmpty(resourceName, "Resource name");

        LOGGER.debug("Classpath resource specified: " + resourceName);
        return from(clazz(type, resourceName));
    }

    public LifeConfig from(ConfigSource source) {
        mustNotBeNull(source, "Config source");

        this.source = source;

        LOGGER.debug("ConfigSource inserted of type " + source.getClass());
        return this;
    }

    /**
     * Marks the configuration being created as live which
     * means that before every accessing a property, a test
     * will be performed to check whether the content of the
     * source has changed or not, and will be reloaded in case
     * it has changed.
     *
     * @return the same LifeConfig
     */
    public LifeConfig live() {
        this.isLife = true;

        LOGGER.debug("Live configuration specified.");
        return this;
    }

    /**
     * Sets java property as a format of configuration. Separator
     * of items is comma.
     *
     * @return the same LifeConfig
     */
    public LifeConfig javaProperties() {
        return format(new JavaPropertyFormat());
    }

    /**
     * Sets java property file as a format of configuration.
     *
     * @param listItemsSeparatorRegex a regular expression of a
     * items separator must not be null or empty.
     *
     * @return the same LifeConfig instance
     */
    public LifeConfig javaProperties(String listItemsSeparatorRegex) {
        mustNotBeNullOrEmpty(listItemsSeparatorRegex, "List item separator");

        return format(new JavaPropertyFormat(listItemsSeparatorRegex));
    }

    /**
     * Sets HOCON as a format of configuration.
     *
     * @return the same LifeConfig instance
     *
     * @see hocon
     */
    public LifeConfig hocon() {
        return format(new HoconFormat());
    }

    /**
     * Sets Yaml as a format of configuration.
     *
     * @return the same LifeConfig instance
     *
     * @see yaml
     */
    public LifeConfig yaml() {
        return format(new YamlFormat());
    }

    /**
     * Sets custom format of configuration.
     *
     * @param format must not be null.
     *
     * @return the same LifeConfig instance
     */
    public LifeConfig format(ConfigFormat format) {
        mustNotBeNull(format, "Config format");

        this.format = format;
        LOGGER.debug("Configuration format specified: " + format.getClass());
        return this;
    }

    /**
     * Prepares a new implementation of configuration interface (dynamically).
     *
     * @return never null
     */
    public T load() {
        LOGGER.debug("----------------------------");
        LOGGER.debug("Creating configuration proxy");
        LOGGER.debug("----------------------------");

        checkAllInserted();

        new ConfigInterfaceValidator(converterRepository).validate(type);

        LOGGER.debug("Validation of type " + type + " was successful");

        PropertyKeyResolver keyResolver = prefixAnnotationBefore(standardMethodKeyResolver());
        ConfigReader reader = reader(converterRepository);

        return ConfigProxyAssembler.forType(type)
                .configReader(reader)
                .keyResolver(keyResolver)
                .life(isLife)
                .assemble();
    }

    private void checkAllInserted() {
        if (source == null) {
            throw new IllegalStateException("Configuration source has not been inserted.");
        }
        if (format == null) {
            throw new IllegalStateException("Format of a configuration has not been inserted. Use format()/yaml()/hocon()/javaConfig");
        }
    }

    private ConfigReader reader(ConverterRepository converterRepository) {
        ConverterProvider conversion = conversion(converterRepository);
        ConfigReader convertingReader = new ConvertingConfigReader(source, format, conversion);
        return new ReloadingConfigReader(convertingReader, () -> source.lastModifiedMillis(), isLife);
    }

    private ConverterProvider conversion(ConverterRepository converterRepository) {
        ConverterProvider throwingExceptionConversion = new ConverterNotFoundThrowingConverterProvider();
        ConverterProvider defaultConversion = new ViaStringConverter(throwingExceptionConversion, converterRepository);
        ConverterProvider primitiveTypesConversion = new PrimitiveAndBoxedTypesConverterProvider(defaultConversion);
        ConverterProvider sameTypeConversion = new SameTypesConverterProvider(primitiveTypesConversion);
        ConverterProvider customConverterConversion = new RegisteredConverterProvider(sameTypeConversion, converterRepository);
        ConverterProvider annotationConversion = new ConverterAnnotationProvider(customConverterConversion);

        return annotationConversion;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy