fathom.conf.Settings.java Source code

Java tutorial

Introduction

Here is the source code for fathom.conf.Settings.java

Source

/*
 * Copyright (C) 2015 the original author or 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 fathom.conf;

import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValue;
import fathom.Constants;
import fathom.exception.FathomException;
import fathom.utils.ClassUtil;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class Settings {

    private static Logger log = LoggerFactory.getLogger(Settings.class);
    private final int defaultJmxPort = 7091;
    private final int defaultHttpPort = 8080;
    private final int defaultHttpsPort = 0;
    private final int defaultAjpPort = 0;
    private final String defaultContextPath = "/";
    private final String defaultListenAddress = "0.0.0.0";
    private final String defaultUploadFilesLocation = System.getProperty("java.io.tmpdir");
    private final long defaultUploadFilesMaxSize = -1L;
    private final Config config;
    private final Properties overrides;
    private String profile = "default";
    private Constants.Mode mode;

    public Settings() {
        this(Constants.Mode.PROD);
    }

    public Settings(Constants.Mode mode) {
        this(mode, new String[0]);
    }

    public Settings(String[] args) {
        this(Constants.Mode.PROD, args);
    }

    public Settings(Constants.Mode mode, String[] args) {
        this.mode = mode;
        this.overrides = new Properties();

        applyArgs(args);
        this.config = loadConfig();
    }

    private Config loadConfig() {
        log.info("Runtime profile is '{}'", profile);

        // start with an empty config
        Config runtimeConfig = ConfigFactory.empty();

        // merge the classpath config file
        URL configFileUrl = ClassUtil.getResource(String.format("conf/%s.conf", profile));
        if (configFileUrl == null) {
            log.warn("Failed to find Fathom config 'classpath:conf/{}.conf'", profile);
        } else {
            Config classpathConfig = loadConfig(configFileUrl);
            if (classpathConfig.isEmpty()) {
                log.warn("Empty config '{}'", configFileUrl);
            } else {
                runtimeConfig = classpathConfig.withFallback(runtimeConfig);
                log.debug("Loaded config '{}'", configFileUrl);
            }
        }

        // merge an external config file
        try {
            Path path = Paths.get(System.getProperty("user.dir"), String.format("%s.conf", profile));
            if (path.toFile().exists()) {
                URL externalUrl = path.toUri().toURL();
                Config externalConfig = loadConfig(externalUrl);
                if (externalConfig != null) {
                    runtimeConfig = externalConfig.withFallback(runtimeConfig);
                    log.debug("Loaded config '{}'", externalUrl);
                }
            } else {
                log.debug("Failed to find working directory config file '{}'", path);
            }
        } catch (MalformedURLException e) {
            log.error("Failed to parse working directory config file!", e);
        }

        // filter the merged runtime config by mode
        String modeName = mode.name().toLowerCase();
        if (runtimeConfig.hasPath(modeName)) {
            Config runtimeModeConfig = runtimeConfig.getConfig(modeName).withFallback(runtimeConfig);
            return runtimeModeConfig.resolve();
        }

        // resolve all substitutions
        return runtimeConfig.resolve();
    }

    private Config loadConfig(URL url) {
        if (url == null) {
            return ConfigFactory.empty();
        }

        Config config = null;
        try (InputStreamReader reader = new InputStreamReader(url.openStream())) {
            config = ConfigFactory.parseReader(reader);
        } catch (IOException e) {
            throw new FathomException(e, "Failed to parse config file '{}'", url);
        }
        return config;
    }

    public boolean isDev() {
        return Constants.Mode.DEV == mode;
    }

    public boolean isTest() {
        return Constants.Mode.TEST == mode;
    }

    public boolean isProd() {
        return Constants.Mode.PROD == mode;
    }

    public String getApplicationName() {
        return Optional.fromNullable(Strings.emptyToNull(getString(Setting.application_name, null)))
                .or("<APPLICATION>");
    }

    public String getApplicationVersion() {
        return Optional.fromNullable(Strings.emptyToNull(getString(Setting.application_version, null)))
                .or("<VERSION>");
    }

    public String getApplicationPackage() {
        return Strings.emptyToNull(getString(Setting.application_package, ""));
    }

    public URL getFileUrl(String name, String defaultValue) {
        String file = getString(name, defaultValue);
        if (Strings.isNullOrEmpty(file)) {
            return null;
        }

        try {
            if (file.startsWith("classpath:")) {
                return ClassUtil.getResource(file.substring("classpath:".length()));
            } else if (file.startsWith("url:")) {
                return new URL(file.substring("url:".length()));
            } else if (file.startsWith("file:")) {
                return new URL(file.substring("file:".length()));
            } else {
                return new URL(file);
            }
        } catch (IOException e) {
            throw new FathomException(e, "Failed to create '{}' URL from '{}'", name, file);
        }
    }

    @Option(name = "--applicationUrl", metaVar = "URL", usage = "The URL to advertise to clients.")
    public Settings applicationUrl(String url) {
        this.overrideSetting(Setting.application_url, url);

        return this;
    }

    /**
     * Returns the preferred application url, if no application url is specified use the Fathom url.
     *
     * @return the preferred application url
     */
    public String getApplicationUrl() {
        return getString(Setting.application_url, getFathomUrl());
    }

    /**
     * Returns the url of the Fathom application based on the listen address, port, and context path.
     * This url is intended for integration testing and will return an http url over https even if both
     * are configured.
     *
     * @return the fathom url
     */
    public String getFathomUrl() {
        if (getHttpPort() > 0) {
            int port = getHttpPort();
            if (port == 80) {
                return String.format("http://%s%s", getHttpListenAddress(), getContextPath());
            }
            return String.format("http://%s:%s%s", getHttpListenAddress(), getHttpPort(), getContextPath());
        } else if (getHttpsPort() > 0) {
            int port = getHttpsPort();
            if (port == 443) {
                return String.format("https://%s%s", getHttpsListenAddress(), getContextPath());
            }
            return String.format("https://%s:%s%s", getHttpsListenAddress(), port, getContextPath());
        }
        return null;
    }

    /**
     * Returns the preferred host url (http is favored over https).
     * Generally this url is used for integration testing.
     *
     * @return the preferred host url
     * @deprecated Use getFathomUrl() or getApplicationUrl() instead.
     */
    @Deprecated
    public String getUrl() {
        return getFathomUrl();
    }

    public void applyArgs(final String... args) {
        if (args == null || args.length == 0) {
            return;
        }

        final CmdLineParser parser = new CmdLineParser(this);
        try {

            parser.parseArgument(args);

        } catch (CmdLineException e) {
            // handling of wrong arguments
            StringWriter sw = new StringWriter();
            sw.append(Constants.getLogo());
            sw.append("\n");
            sw.append(String.format("Arguments:%n    %s%n%n", Arrays.toString(args)));
            sw.append(String.format("Error:%n    %s%n%n", e.getMessage()));
            sw.append("Options:\n\n");
            parser.printUsage(sw, null);
            log.error(sw.toString());
            throw new FathomException("Failed to parse args: {}", args);
        }
    }

    public String getProfile() {
        return profile;
    }

    @Option(name = "--profile", metaVar = "PROFILE", usage = "Specify the settings profile name\n"
            + "e.g. 'application' to load the 'application.conf' settings\n"
            + "     'master' to load the 'master.conf' settings")
    public Settings profile(String profile) {
        this.profile = profile;

        return this;
    }

    public Constants.Mode getMode() {
        return mode;
    }

    // careful changing this flag (see getMode(String... args);
    @Option(name = "--mode", metaVar = "MODE", usage = "Fathom runtime mode.\n" + "PROD = production mode\n"
            + "TEST = test mode\n" + "DEV = development mode")
    public Settings mode(Constants.Mode mode) {
        this.mode = mode;

        return this;
    }

    /**
     * Returns the listen address of Undertow.
     *
     * @return thre listen address of Undertow
     * @deprecated Use the transport specific method.
     */
    @Deprecated
    public String getHost() {
        if (getHttpPort() > 0) {
            return getHttpListenAddress();
        } else if (getHttpsPort() > 0) {
            return getHttpsListenAddress();
        } else {
            return getAjpListenAddress();
        }
    }

    /**
     * Sets the host interface for binding all transports.
     *
     * @param host
     * @return this
     * @deprecated Please use the transport-specific settings.
     */
    @Deprecated
    public Settings host(String host) {
        throw new FathomException(
                "Setting the host address is no longer supported!  Please use the transport-specific settings.");
    }

    public int getJmxPort() {
        return getInteger(Setting.jmx_port, defaultJmxPort);
    }

    @Option(name = "--jmxPort", metaVar = "PORT", usage = "Port for serving the JMX registry & data.\nPORT <= 0 will disable this transport.")
    public Settings jmxPort(int port) {
        this.overrideSetting(Setting.jmx_port, port);

        return this;
    }

    public int getHttpPort() {
        return getInteger(Setting.undertow_httpPort, defaultHttpPort);
    }

    @Option(name = "--httpPort", metaVar = "PORT", usage = "Port for serving HTTP.\nPORT <= 0 will disable this transport.")
    public Settings httpPort(int port) {
        this.overrideSetting(Setting.undertow_httpPort, port);

        return this;
    }

    public String getHttpListenAddress() {
        return getString(Setting.undertow_httpListenAddress, defaultListenAddress);
    }

    @Option(name = "--httpListenAddress", metaVar = "ADDRESS", usage = "Interface to use for serving HTTP.")
    public Settings httpListenAddress(String address) {
        this.overrideSetting(Setting.undertow_httpListenAddress, address);

        return this;
    }

    public int getHttpsPort() {
        return getInteger(Setting.undertow_httpsPort, defaultHttpsPort);
    }

    @Option(name = "--httpsPort", metaVar = "PORT", usage = "Port for serving HTTPS.\nPORT <= 0 will disable this transport.")
    public Settings httpsPort(int port) {
        this.overrideSetting(Setting.undertow_httpsPort, port);

        return this;
    }

    public String getHttpsListenAddress() {
        return getString(Setting.undertow_httpsListenAddress, defaultListenAddress);
    }

    @Option(name = "--httpsListenAddress", metaVar = "ADDRESS", usage = "Interface to use for serving HTTPS.")
    public Settings httpsListenAddress(String address) {
        this.overrideSetting(Setting.undertow_httpsListenAddress, address);

        return this;
    }

    public int getAjpPort() {
        return getInteger(Setting.undertow_ajpPort, defaultAjpPort);
    }

    @Option(name = "--ajpPort", metaVar = "PORT", usage = "Port for serving AJP.\nPORT <= 0 will disable this transport.")
    public Settings ajpPort(int port) {
        this.overrideSetting(Setting.undertow_ajpPort, port);

        return this;
    }

    public String getAjpListenAddress() {
        return getString(Setting.undertow_ajpListenAddress, defaultListenAddress);
    }

    @Option(name = "--ajpListenAddress", metaVar = "ADDRESS", usage = "Interface to use for serving AJP.")
    public Settings ajpListenAddress(String address) {
        this.overrideSetting(Setting.undertow_ajpListenAddress, address);

        return this;
    }

    public String getContextPath() {
        return getString(Setting.undertow_contextPath, defaultContextPath);
    }

    @Option(name = "--context", metaVar = "PATH", usage = "Sets the context path")
    public Settings contextPath(String contextPath) {
        this.overrideSetting(Setting.undertow_contextPath, contextPath/*StringUtils.addStart(contextPath, "/")*/);

        return this;
    }

    public String getUploadFilesLocation() {
        return getString(Setting.application_uploadLocation, defaultUploadFilesLocation);
    }

    public Settings uploadFilesLocation(String uploadFilesLocation) {
        this.overrideSetting(Setting.application_uploadLocation, uploadFilesLocation);

        return this;
    }

    public long getUploadFilesMaxSize() {
        return getLong(Setting.application_uploadMaxSize, defaultUploadFilesMaxSize);
    }

    public Settings uploadFilesMaxSize(long uploadFilesMaxSize) {
        this.overrideSetting(Setting.application_uploadMaxSize, uploadFilesMaxSize);

        return this;
    }

    public String getKeystoreFile() {
        return getString(Setting.undertow_keystoreFile, null);
    }

    public Settings keystoreFile(String keystoreFile) {
        this.overrideSetting(Setting.undertow_keystoreFile, keystoreFile);

        return this;
    }

    public String getKeystorePassword() {
        return getString(Setting.undertow_keystorePassword, null);
    }

    public Settings keystorePassword(String keystorePassword) {
        this.overrideSetting(Setting.undertow_keystorePassword, keystorePassword);

        return this;
    }

    public String getTruststoreFile() {
        return getString(Setting.undertow_truststoreFile, null);
    }

    public Settings truststoreFile(String truststoreFile) {
        this.overrideSetting(Setting.undertow_truststoreFile, truststoreFile);

        return this;
    }

    public String getTruststorePassword() {
        return getString(Setting.undertow_truststorePassword, null);
    }

    public Settings truststorePassword(String truststorePassword) {
        this.overrideSetting(Setting.undertow_truststorePassword, truststorePassword);

        return this;
    }

    public String getRequiredString(Enum<?> key) {
        return getRequiredString(key.toString());
    }

    public String getString(Enum<?> key, String defaultValue) {
        return getString(key.toString(), defaultValue);
    }

    public int getInteger(Enum<?> key, int defaultValue) {
        return getInteger(key.toString(), defaultValue);
    }

    public long getLong(Enum<?> key, long defaultValue) {
        return defaultValue;//getLong(key.toString(), defaultValue);
    }

    public boolean getBoolean(Enum<?> key, boolean defaultValue) {
        return getBoolean(key.toString(), defaultValue);
    }

    public long getBytes(Enum<?> key, String defaultValue) {
        return getBytes(key.toString(), defaultValue);
    }

    public void overrideSetting(Enum<?> key, String defaultValue) {
        overrideSetting(key.toString(), defaultValue);
    }

    public void overrideSetting(Enum<?> key, int defaultValue) {
        overrideSetting(key.toString(), defaultValue);
    }

    public void overrideSetting(Enum<?> key, long defaultValue) {
        overrideSetting(key.toString(), defaultValue);
    }

    public void overrideSetting(Enum<?> key, boolean defaultValue) {
        overrideSetting(key.toString(), defaultValue);
    }

    private String extractScheme(String url) {
        try {
            return URI.create(url).getScheme();
        } catch (Exception e) {
        }
        return null;
    }

    public String getApplicationScheme() {
        String fathomScheme = extractScheme(getFathomUrl());
        String applicationScheme = extractScheme(getApplicationUrl());
        return Optional.fromNullable(Strings.emptyToNull(applicationScheme)).or(fathomScheme);
    }

    public int getApplicationPort() {
        int fathomPort = extractPort(getFathomUrl());
        int applicationPort = extractPort(getApplicationUrl());
        return applicationPort > 0 ? applicationPort : fathomPort;
    }

    private int extractPort(String url) {
        try {
            URI uri = URI.create(url);
            int port = uri.getPort();
            if (port > 0) {
                return port;
            }
            if ("https".equals(uri.getScheme())) {
                return 443;
            } else if ("http".equals(uri.getScheme())) {
                return 80;
            }
        } catch (Exception e) {
        }
        return 0;
    }

    private String extractHostname(String url) {
        try {
            return URI.create(url).getHost();
        } catch (Exception e) {
        }
        return null;
    }

    public String getApplicationHostname() {
        String fathomHostname = extractHostname(getFathomUrl());
        String applicationHostname = extractHostname(getApplicationUrl());
        return Optional.fromNullable(Strings.emptyToNull(applicationHostname))
                .or(Optional.fromNullable(Strings.emptyToNull(fathomHostname)).or(getLocalHostname()));
    }

    /**
     * Formerly set the application hostname.
     *
     * @param hostname
     *
     * @return this
     * @deprecated does nothing
     */
    @Deprecated
    public Settings hostname(String hostname) {
        throw new FathomException("Setting the application hostname is no longer supported!");
    }

    public String getLocalHostname() {
        // try InetAddress.LocalHost first;
        // NOTE -- InetAddress.getLocalHost().getHostName() will not work in
        // certain environments.
        try {
            String result = InetAddress.getLocalHost().getHostName();
            if (!Strings.isNullOrEmpty(result))
                return result;
        } catch (UnknownHostException e) {
            // failed; try alternate means.
        }

        // try environment properties.
        //
        String host = System.getenv("COMPUTERNAME");
        if (host != null)
            return host;
        host = System.getenv("HOSTNAME");
        if (host != null)
            return host;

        // undetermined.
        return "Fathom";
    }

    public Config getConfig() {
        return config;
    }

    public Config getConfig(String name) {
        return config.getConfig(name);
    }

    public String getNonEmptyString(String name, String defaultValue) {
        String value = Optional.fromNullable(Strings.emptyToNull(getString(name, null))).or(defaultValue);
        return value;
    }

    /**
     * Returns the string value for the specified name. If the name does not exist
     * or the value for the name can not be interpreted as a string, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public String getString(String name, String defaultValue) {
        String value = defaultValue;
        if (getConfig().hasPath(name)) {
            value = getConfig().getString(name);
        }
        value = overrides.getProperty(name, value);

        return value;
    }

    /**
     * Returns the boolean value for the specified name. If the name does not
     * exist or the value for the name can not be interpreted as a boolean, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public boolean getBoolean(String name, boolean defaultValue) {
        String value = getString(name, null);
        if (!Strings.isNullOrEmpty(value)) {
            return Boolean.parseBoolean(value.trim());
        }

        return defaultValue;
    }

    /**
     * Returns the integer value for the specified name. If the name does not
     * exist or the value for the name can not be interpreted as an integer, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public int getInteger(String name, int defaultValue) {
        try {
            String value = getString(name, null);
            if (!Strings.isNullOrEmpty(value)) {
                return Integer.parseInt(value.trim().split(" ")[0]);
            }
        } catch (NumberFormatException e) {
            log.warn("Failed to parse integer for " + name + " using default of " + defaultValue);
        }

        return defaultValue;
    }

    /**
     * Returns the long value for the specified name. If the name does not
     * exist or the value for the name can not be interpreted as an long, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public long getLong(String name, long defaultValue) {
        try {
            String value = getString(name, null);
            if (!Strings.isNullOrEmpty(value)) {
                return Long.parseLong(value.trim().split(" ")[0]);
            }
        } catch (NumberFormatException e) {
            log.warn("Failed to parse long for " + name + " using default of " + defaultValue);
        }

        return defaultValue;
    }

    /**
     * Returns the bytes value for the specified name. If the name does not
     * exist or the value for the name can not be interpreted as a size expression, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return value or defaultValue
     */
    public long getBytes(String name, String defaultValue) {
        if (config.hasPath(name)) {
            try {
                long value = config.getBytes(name);
                return value;
            } catch (Exception e) {
                log.warn("Failed to parse bytes for {} using default of {}", name, defaultValue);
            }
        }

        if (!Strings.isNullOrEmpty(defaultValue)) {
            try {
                Config temp = ConfigFactory.parseString(String.format("%s=%s", name, defaultValue));
                long value = temp.getBytes(name);
                return value;
            } catch (Exception e) {
                log.warn("Failed to parse default bytes expression {} for {}", defaultValue, name);
            }
        }
        return 0;
    }

    /**
     * Returns the float value for the specified name. If the name does not
     * exist or the value for the name can not be interpreted as a float, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public float getFloat(String name, float defaultValue) {
        try {
            String value = getString(name, null);
            if (!Strings.isNullOrEmpty(value)) {
                return Float.parseFloat(value.trim().split(" ")[0]);
            }
        } catch (NumberFormatException e) {
            log.warn("Failed to parse float for " + name + " using default of " + defaultValue);
        }

        return defaultValue;
    }

    /**
     * Returns the double value for the specified name. If the name does not
     * exist or the value for the name can not be interpreted as a double, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public double getDouble(String name, double defaultValue) {
        try {
            String value = getString(name, null);
            if (!Strings.isNullOrEmpty(value)) {
                return Double.parseDouble(value.trim().split(" ")[0]);
            }
        } catch (NumberFormatException e) {
            log.warn("Failed to parse double for " + name + " using default of " + defaultValue);
        }

        return defaultValue;
    }

    /**
     * Returns the char value for the specified name. If the name does not exist
     * or the value for the name can not be interpreted as a char, the
     * defaultValue is returned.
     *
     * @param name
     * @param defaultValue
     * @return name value or defaultValue
     */
    public char getChar(String name, char defaultValue) {
        String value = getString(name, null);
        if (!Strings.isNullOrEmpty(value)) {
            return value.trim().charAt(0);
        }

        return defaultValue;
    }

    /**
     * Returns the string value for the specified name.  If the name does not
     * exist an exception is thrown.
     *
     * @param name
     * @return name value
     */
    public String getRequiredString(String name) {
        String value = getString(name, null);
        if (value != null) {
            return value.trim();
        }
        throw new FathomException("Setting '{}' has not been configured!", name);
    }

    /**
     * Returns a list of comma-delimited strings from the specified name.
     *
     * @param name
     * @return list of strings
     */
    public List<String> getStrings(String name) {
        if (!getConfig().hasPath(name)) {
            return Collections.emptyList();
        }
        List<String> stringList = getConfig().getStringList(name);
        return stringList;
    }

    /**
     * Returns a list of comma-delimited integers from the specified name.
     *
     * @param name
     * @return list of integers
     */
    public List<Integer> getIntegers(String name) {
        if (!getConfig().hasPath(name)) {
            return Collections.emptyList();
        }
        List<Integer> ints = getConfig().getIntList(name);
        return ints;
    }

    /**
     * Returns a list of comma-delimited longs from the specified name.
     *
     * @param name
     * @return list of longs
     */
    public List<Long> getLongs(String name) {
        if (!getConfig().hasPath(name)) {
            return Collections.emptyList();
        }
        List<Long> longs = getConfig().getLongList(name);
        return longs;
    }

    /**
     * Gets the duration setting and converts it to milliseconds.
     * <p>
     * The setting must be use one of the following conventions:
     * <ul>
     * <li> n MILLISECONDS
     * <li> n SECONDS
     * <li> n MINUTES
     * <li> n HOURS
     * <li> n DAYS
     * </ul>
     *
     * @param name
     * @return milliseconds
     */
    public long getDuration(String name, TimeUnit timeUnit, long defaultValue) {
        if (!getConfig().hasPath(name)) {
            return defaultValue;
        }
        long duration = getConfig().getDuration(name, timeUnit);
        return duration;
    }

    /**
     * Tests for the existence of a setting.
     *
     * @param name
     * @return true if the setting exists
     */
    public boolean hasSetting(String name) {
        return getConfig().hasPath(name) && getConfig().getValue(name).unwrapped() != null;
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, boolean value) {
        overrides.put(name, "" + value);
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, String value) {
        overrides.put(name, value);
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, char value) {
        overrides.put(name, "" + value);
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, int value) {
        overrides.put(name, "" + value);
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, long value) {
        overrides.put(name, "" + value);
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, float value) {
        overrides.put(name, "" + value);
    }

    /**
     * Override the setting at runtime with the specified value.
     * This change does not persist.
     *
     * @param name
     * @param value
     */
    public void overrideSetting(String name, double value) {
        overrides.put(name, "" + value);
    }

    public Properties toProperties() {
        Properties props = new Properties();
        for (Map.Entry<String, ConfigValue> entry : getConfig().entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue().unwrapped();
            props.put(key, value);
        }
        props.putAll(overrides);
        return props;
    }

    public Properties toProperties(String name) {
        Properties props = new Properties();
        for (Map.Entry<String, ConfigValue> entry : getConfig(name).entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue().unwrapped();
            props.put(key, value);
        }
        String nameGroup = name + ".";
        for (String override : overrides.stringPropertyNames()) {
            if (override.startsWith(nameGroup)) {
                props.put(override, overrides.get(override));
            }
        }
        return props;
    }

    public static enum Setting {
        application_name, application_version, application_package, application_controllersPackage, application_uploadLocation, application_uploadMaxSize, application_url, jcache_preferredProvider, jmx_port, metrics_jvm_enabled, metrics_mbeans_enabled, undertow_ajpPort, undertow_ajpListenAddress, undertow_httpPort, undertow_httpListenAddress, undertow_httpsPort, undertow_httpsListenAddress, undertow_contextPath, undertow_keystoreFile, undertow_keystorePassword, undertow_truststoreFile, undertow_truststorePassword, undertow_ioThreads, undertow_workerThreads, undertow_bufferSize;

        @Override
        public String toString() {
            return name().replace('_', '.');
        }
    }
}