io.stallion.settings.Settings.java Source code

Java tutorial

Introduction

Here is the source code for io.stallion.settings.Settings.java

Source

/*
 * Stallion Core: A Modern Web Framework
 *
 * Copyright (C) 2015 - 2016 Stallion Software LLC.
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 2 of
 * the License, or (at your option) any later version. This program is distributed in the hope that
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details. You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 *
 *
 *
 */

package io.stallion.settings;

import io.stallion.boot.CommandOptionsBase;
import io.stallion.exceptions.ConfigException;
import io.stallion.exceptions.UsageException;
import io.stallion.reflection.PropertyUtils;
import io.stallion.requests.RouteDefinition;
import io.stallion.services.Log;
import io.stallion.settings.childSections.*;
import io.stallion.utils.DateUtils;
import io.stallion.utils.GeneralUtils;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.Column;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Pattern;

import static io.stallion.utils.Literals.empty;
import static io.stallion.utils.Literals.*;

public class Settings implements ISettings {

    // Child sections
    private DbConfig database = null;
    private UserSettings users = null;
    private EmailSettings email = null;
    private CustomSettings custom = null;
    private CloudStorageSettings cloudStorage = null;
    private StyleSettings styles = null;
    private CorsSettings cors = null;
    private OAuthSettings oAuth;
    private SecretsSettings secrets;
    private UserUploadSettings userUploads;

    // Site information
    @SettingMeta()
    private String authorName = null;
    @SettingMeta()
    private String siteName = null;
    @SettingMeta(val = "A Stallion Web Site")
    private String defaultTitle = null;
    @SettingMeta()
    private String metaDescription = null;
    @SettingMeta(val = "Stallion")
    private String metaGenerator = null;
    @SettingMeta(val = "", help = "The email address users of the site should contact in case of problems. This will be publicly viewable.")
    private String supportEmail;
    @SettingMeta(valLong = 1430510589000L)
    private Long appCreatedMillis;

    // Tuning
    @SettingMeta(valLong = 5000000)
    private Long filterCacheSize;

    // File system info
    @SettingMeta()
    private String dataDirectory = null;
    @SettingMeta()
    private String targetFolder = null;

    // Runtime mode config
    @SettingMeta()
    private Boolean localMode;
    @SettingMeta()
    private Boolean devMode;

    @SettingMeta()
    private Boolean lightweightMode;

    @SettingMeta()
    private String logFile;
    @SettingMeta()
    private Boolean logToConsole;
    @SettingMeta()
    private Boolean logToFile;
    @SettingMeta()
    private Boolean debug;
    @SettingMeta()
    private Boolean emailErrors;
    @SettingMeta()
    private StrictnessLevel strictnessLevel;
    @SettingMeta(val = "INFO")
    private String logLevel;
    private Map<String, String> packageLogLevels = new HashMap<>();
    private Integer nodeNumber = 1;
    @SettingMeta(val = "x-Real-Ip", help = "The HTTP header that contains the original client IP address, by default, with nginx proxying, this is x-Real-Ip")
    private String ipHeaderName;

    private String env;
    @SettingMeta()
    private Boolean bundleDebug;

    // Routes and rewrites
    @SettingMeta(val = "SAMEORIGIN")
    private String xFrameOptions;
    @SettingMeta(cls = ArrayList.class)
    private List<RouteDefinition> routes = new ArrayList<>();
    @SettingMeta(cls = HashMap.class)
    private Map<String, String> redirects = null;
    @SettingMeta(cls = HashMap.class)
    private Map<String, String> rewrites = null;
    @SettingMeta(cls = ArrayList.class)
    private List<String[]> rewritePatterns = null;
    @SettingMeta(cls = ArrayList.class)
    private List<Map.Entry<Pattern, String>> rewriteCompiledPatterns = null;
    @SettingMeta(cls = ArrayList.class)
    private List<SecondaryDomain> secondaryDomains;
    private Map<String, SecondaryDomain> secondaryDomainByDomain = map();
    @SettingMeta(cls = ArrayList.class)
    private List<AssetPreprocessorConfig> assetPreprocessors;

    // Web Serving
    @SettingMeta(valInt = 8090)
    private Integer port;
    @SettingMeta(val = "http://localhost:{port}")
    private String cdnUrl;
    @SettingMeta(val = "http://localhost:{port}")
    private String siteUrl;
    @SettingMeta(val = "25M")
    private String nginxClientMaxBodySize;
    @SettingMeta(val = "3600")
    private String nginxProxyReadTimeout;

    // Default code directories and files
    @SettingMeta(cls = ArrayList.class)
    private List<ContentFolder> folders;
    @SettingMeta(val = "page.jinja")
    private String pageTemplate = "page.jinja";

    // Email

    // Publishing
    //@SettingMeta(cls=ArrayList.class)
    //private List<PublishingConfig> publishing;

    // Time
    @SettingMeta()
    private String timeZone;// = ZoneId.systemDefault().toString();
    @SettingMeta()
    private ZoneId timeZoneId;// = ZoneId.systemDefault();

    // Anti-spam
    @SettingMeta(val = "")
    private String antiSpamSecret;
    @SettingMeta(valBoolean = false)
    private Boolean disableFormSubmissions;

    // Health
    @SettingMeta(cls = ArrayList.class)
    private List<String[]> healthCheckEndpoints = new ArrayList<>();
    @SettingMeta()
    private String healthCheckSecret;

    private static Settings _instance;

    public boolean isEmailConfigured() {
        return getEmail() != null && !empty(getEmail().getHost());
    }

    public static boolean isNull() {
        return _instance == null;
    }

    public static void shutdown() {
        _instance = null;
    }

    public static Settings instance() {
        if (_instance == null) {
            throw new UsageException("You must call load() before instance().");
        }
        return _instance;
    }

    public static Settings init(String env, CommandOptionsBase options) {
        _instance = new SettingsLoader().loadSettings(env, options.getTargetPath(), "stallion", Settings.class,
                options);

        _instance.setCdnUrl(_instance.getCdnUrl().replace("{port}", _instance.getPort().toString()));
        _instance.setSiteUrl(_instance.getSiteUrl().replace("{port}", _instance.getPort().toString()));
        return _instance;
    }

    public void assignDefaults() {
        if (getLocalMode() == null) {
            if (empty(System.getenv().getOrDefault("STALLION_DEPLOY_TIME", ""))) {
                setLocalMode(true);
            } else {
                setLocalMode(false);
            }
        }

        if (bundleDebug == null) {
            bundleDebug = getLocalMode();
        }

        if (getDebug() == null) {
            if (getEnv().equals("prod") && !getLocalMode()) {
                setDebug(false);
            } else {
                setDebug(true);
            }
        }

        if (timeZone == null) {
            timeZone = ZoneId.systemDefault().toString();
        }
        if (timeZoneId == null) {
            timeZoneId = ZoneId.of(timeZone);
        }

        if (logFile == null) {
            String nowString = DateUtils.formatNow("yyyy-MM-dd-HHmmss");
            String base = "";
            try {
                if (!empty(siteUrl)) {
                    base = new URL(siteUrl.replace(":{port}", "")).getHost();
                }
            } catch (IOException e) {
                Log.exception(e, "Error parsing siteUrl " + siteUrl);
            }
            logFile = "/tmp/log/stallion/" + base + "-" + nowString + "-"
                    + StringUtils.strip(GeneralUtils.slugify(targetFolder), "-") + ".log";
        }
        if (logToConsole == null) {
            logToConsole = getLocalMode();
        }
        if (logToFile == null) {
            logToFile = !logToConsole;
        }

        if (getEmailErrors() == null) {
            if ((getEnv().equals("prod") || getEnv().equals("qa")) && !getLocalMode()) {
                setEmailErrors(true);
            } else {
                setEmailErrors(false);
            }
        }

        if (getStrictnessLevel() == null) {
            if (getEnv().equals("prod") && !getLocalMode()) {
                setStrictnessLevel(StrictnessLevel.LAX);
            } else {
                setStrictnessLevel(StrictnessLevel.STRICT);
            }
        }

        if (!empty(secondaryDomains)) {
            secondaryDomainByDomain = map();
            for (SecondaryDomain d : secondaryDomains) {
                secondaryDomainByDomain.put(d.getDomain(), d);
            }
        }

        if (!StringUtils.isEmpty(getDataDirectory())) {
            if (!getDataDirectory().startsWith("/")) {
                setDataDirectory(targetFolder + "/" + getDataDirectory());
            }
        } else {
            setDataDirectory(targetFolder + "/app-data");
        }
        if (getDataDirectory().endsWith("/")) {
            setDataDirectory(getDataDirectory().substring(0, getDataDirectory().length() - 1));
        }

        if (getRewrites() != null) {
            // THe Toml library has a bug whereby if a map key is quoted, it keeps the
            // quotes as part of the key, rather than using the String inside the quotes
            Set<String> keys = new HashSet<>(getRewrites().keySet());
            for (String key : keys) {
                if (key.startsWith("\"") && key.endsWith("\"")) {
                    getRewrites().put(key.substring(1, key.length() - 1), getRewrites().get(key));
                }
            }
        }

        if (getRedirects() != null) {
            // THe Toml library has a bug whereby if a map key is quoted, it keeps the
            // quotes as part of the key, rather than using the String inside the quotes
            Set<String> keys = new HashSet<>(getRedirects().keySet());
            for (String key : keys) {
                if (key.startsWith("\"") && key.endsWith("\"")) {
                    getRedirects().put(key.substring(1, key.length() - 1), getRedirects().get(key));
                }
            }
        }

        if (getRewritePatterns() != null && getRewritePatterns().size() > 0) {
            if (rewriteCompiledPatterns == null) {
                rewriteCompiledPatterns = new ArrayList();
            }
            for (String[] entry : getRewritePatterns()) {
                if (entry.length != 2) {
                    Log.warn("Invalid rewritePatterns entry, size should be 2 but is {0} {1}", entry.length, entry);
                }
                Pattern pattern = Pattern.compile(entry[0]);
                Map.Entry<Pattern, String> kv = new AbstractMap.SimpleEntry<Pattern, String>(pattern, entry[1]);
                getRewriteCompiledPatterns().add(kv);
            }
        }

        if (getEmail() != null) {
            // By default, in debug mode, we do not want to send real emails to people
            if (getEmail().getRestrictOutboundEmails() == null) {
                getEmail().setRestrictOutboundEmails(getDebug());
            }
            if (getEmail().getOutboundEmailTestAddress() == null) {
                if (getEmail().getAdminEmails() != null && getEmail().getAdminEmails().size() > 0) {
                    getEmail().setOutboundEmailTestAddress(getEmail().getAdminEmails().get(0));
                }
            }
            if (!empty(getEmail().getAllowedTestingOutboundEmailPatterns())) {
                if (getEmail().getAllowedTestingOutboundEmailCompiledPatterns() == null) {
                    getEmail().setAllowedTestingOutboundEmailCompiledPatterns(new ArrayList<>());
                }
                for (String emailPattern : getEmail().getAllowedTestingOutboundEmailPatterns()) {
                    getEmail().getAllowedTestingOutboundEmailCompiledPatterns().add(Pattern.compile(emailPattern));
                }
            }
        }

        if (getSecrets() == null) {
            setSecrets(new SecretsSettings());
        }

        if (new File(targetFolder + "/pages").isDirectory()) {
            if (folders == null) {
                folders = new ArrayList<>();
            }
            folders.add(new ContentFolder().setPath(targetFolder + "/pages").setType("markdown")
                    .setItemTemplate(getPageTemplate()));
        }

        if (userUploads != null && empty(userUploads.getUploadsDirectory())) {
            userUploads.setUploadsDirectory(getDataDirectory() + "/st-user-file-uploads");
        }

    }

    /**
     * Gets the scheme (https or http) for a given secondary domain
     * @return
     */
    public String getSchemeForSecondaryDomain(String domain) {
        SecondaryDomain d = secondaryDomainByDomain.get(domain);
        return or(d.getScheme(), getSiteUrlScheme());
    }

    /**
     * Get siteUrl scheme
     * @return
     */
    public String getSiteUrlScheme() {
        if (getSiteUrl().startsWith("https:")) {
            return "https";
        } else {
            return "http";
        }
    }

    public SecondaryDomain getSecondaryDomainByDomain(String domain) {
        return secondaryDomainByDomain.get(domain);
    }

    /**
     * In strict mode, more errors will be exceptions that stop all processing. For instance, missing template variables
     * will cause exceptions rather than being ignored.
     * @return
     */
    public boolean isStrict() {
        return StrictnessLevel.STRICT.equals(getStrictnessLevel());
    }

    /**
     * ContentFolders are folders in your local site directory that are to be registered with the data access controller.
     * The .txt will be converted into accessible web pages in your site.
     * @return
     */
    public List<ContentFolder> getFolders() {
        return folders;
    }

    public void setFolders(List<ContentFolder> folders) {
        this.folders = folders;
    }

    /**
     * The database configuration
     * @return
     */
    public DbConfig getDatabase() {
        return database;
    }

    public void setDatabase(DbConfig database) {
        this.database = database;
    }

    /**
     * The user configuration
     * @return
     */
    public UserSettings getUsers() {
        return users;
    }

    public void setUsers(UserSettings users) {
        this.users = users;
    }

    public UserUploadSettings getUserUploads() {
        return userUploads;
    }

    public Settings setUserUploads(UserUploadSettings userUploads) {
        this.userUploads = userUploads;
        return this;
    }

    /**
     * Email configuration for Stallion sending emails via SMTP
     * @return
     */
    public EmailSettings getEmail() {
        return email;
    }

    public void setEmail(EmailSettings email) {
        this.email = email;
    }

    /**
     * Cross-Origin Request configuration -- allow endpoints to respond to cross-origin requests.
     * @return
     */
    public CorsSettings getCors() {
        return cors;
    }

    public Settings setCors(CorsSettings cors) {
        this.cors = cors;
        return this;
    }

    /**
     * Where all data stored to flat-file by the Controllers and Persisters will actually live in the
     * file system. This will be "app-data" under the site directory by default.
     * @return
     */
    public String getDataDirectory() {
        return dataDirectory;
    }

    public void setDataDirectory(String dataDirectory) {
        this.dataDirectory = dataDirectory;
    }

    /**
     * A hashtable of arbitrary user defined settings.
     * @return
     */
    public CustomSettings getCustom() {
        return custom;
    }

    public void setCustom(CustomSettings custom) {
        this.custom = custom;
    }

    /**
     * The default template to use to render pages
     *
     * @return
     */
    public String getPageTemplate() {
        return pageTemplate;
    }

    public void setPageTemplate(String pageTemplate) {
        this.pageTemplate = pageTemplate;
    }

    /**
     * The base URL for CDN (Content Delivery Network) that will pass through to your site. This is used
     * by AssetsController.url() or AssetsController.bundle() to build URL's to your assets. If empty, will use
     * the site URL instead. Using a CDN can improve performance as it will cache requested assets in data centers
     * nearer to the end user.
     *
     * @return
     */
    public String getCdnUrl() {
        return cdnUrl;
    }

    public void setCdnUrl(String cdnUrl) {
        this.cdnUrl = cdnUrl;
    }

    /**
     * The base URL at which the site lives. Used to build links internally, also used for checking cross-origin
     * requests.
     *
     * @return
     */
    public String getSiteUrl() {
        return siteUrl;
    }

    public void setSiteUrl(String siteUrl) {
        this.siteUrl = siteUrl;
    }

    public List<RouteDefinition> getRoutes() {
        return routes;
    }

    public void setRoutes(List<RouteDefinition> routes) {
        this.routes = routes;
    }

    /**
     * Run in debug mode. True by default when env=local. In debug mode, the full stack trace of exceptions
     * will be rendered to the HTML output.
     *
     * @return
     */
    public Boolean getDebug() {
        return debug;
    }

    public void setDebug(Boolean debug) {
        this.debug = debug;
    }

    /**
     * Should be a time zone parseable by ZoneId.of() Used by the DateUtils.renderLocalDate() method when there
     * is no time zone for the particular user.
     *
     * @return
     */
    public String getTimeZone() {
        return timeZone;
    }

    public void setTimeZone(String timeZone) {
        this.timeZone = timeZone;
    }

    public ZoneId getTimeZoneId() {
        return timeZoneId;
    }

    public void setTimeZoneId(ZoneId timeZoneId) {
        this.timeZoneId = timeZoneId;
    }

    /**
     * Usually set by the command line options, rather than the stallion.toml file.
     *
     * If true, templates and assets will automatically be checked for changes from the original source
     * and re-compiled if they have changed.
     *
     * @return
     */
    public Boolean getDevMode() {
        return devMode;
    }

    public void setDevMode(Boolean devMode) {
        this.devMode = devMode;
    }

    /**
     * Set the log level - INFO, FINE, FINER, FINEST
     *
     * @return
     */
    public String getLogLevel() {
        return logLevel;
    }

    public void setLogLevel(String logLevel) {
        this.logLevel = logLevel;
    }

    public Map<String, String> getPackageLogLevels() {
        return packageLogLevels;
    }

    public void setPackageLogLevels(Map<String, String> packageLogLevels) {
        this.packageLogLevels = packageLogLevels;
    }

    /**
     * The current environment name, set by command line options.
     *
     * @return
     */
    public String getEnv() {
        return env;
    }

    public void setEnv(String env) {
        this.env = env;
    }

    /**
     * True by default if in debug mode, false on production. If true,
     * bundle files will be included on the page as individual source files, rather than as a concatenated,
     * minified file.
     *
     * @return
     */
    public Boolean getBundleDebug() {
        return bundleDebug;
    }

    public void setBundleDebug(Boolean bundleDebug) {
        this.bundleDebug = bundleDebug;
    }

    /**
     * The name of your site, accessible in templates via {{ site.name }}
     *
     * @return
     */
    public String getSiteName() {
        return siteName;
    }

    public void setSiteName(String siteName) {
        this.siteName = siteName;
    }

    /**
     * The default title of your site, will be used in the HTML title tag in the default base template
     * if no other title for a page is defined.
     *
     *
     * @return
     */
    public String getDefaultTitle() {
        return defaultTitle;
    }

    public void setDefaultTitle(String defaultTitle) {
        this.defaultTitle = defaultTitle;
    }

    /**
     * The default meta tag description value. Used if there is no override for the particular page or endpoint.
     * @return
     */
    public String getMetaDescription() {
        return metaDescription;
    }

    public void setMetaDescription(String metaDescription) {
        this.metaDescription = metaDescription;
    }

    /**
     * Get the name of the author of the site
     *
     * @return
     */
    public String getAuthorName() {
        return authorName;
    }

    public void setAuthorName(String authorName) {
        this.authorName = authorName;
    }

    /**
     * The port the server should run on. Usually set by the command line options.
     *
     * @return
     */
    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    /**
     * The root site folder that contains the conf directory and the conf/stallion.toml files.
     * This is always set via the command line options, or defaults to the current working directory.
     *
     * @return
     */
    public String getTargetFolder() {
        return targetFolder;
    }

    public void setTargetFolder(String targetFolder) {
        this.targetFolder = targetFolder;
    }

    public StrictnessLevel getStrictnessLevel() {
        return strictnessLevel;
    }

    public void setStrictnessLevel(StrictnessLevel strictnessLevel) {
        this.strictnessLevel = strictnessLevel;
    }

    /**
     * A list of paths that will be checked for a 200 response when the health check
     * is run.
     *
     * @return
     */
    public List<String[]> getHealthCheckEndpoints() {
        return healthCheckEndpoints;
    }

    public void setHealthCheckEndpoints(List<String[]> healthCheckEndpoints) {
        this.healthCheckEndpoints = healthCheckEndpoints;
    }

    /**
     * A secret token that must be passed in when accessing the health endpoints.
     *
     * @return
     */
    public String getHealthCheckSecret() {
        return healthCheckSecret;
    }

    public void setHealthCheckSecret(String healthCheckSecret) {
        this.healthCheckSecret = healthCheckSecret;
    }

    /**
     * If true, exceptions will emailed to the admin defined in the email configuration.
     *
     * @return
     */
    public Boolean getEmailErrors() {
        return emailErrors;
    }

    public void setEmailErrors(Boolean emailErrors) {
        this.emailErrors = emailErrors;
    }

    /**
     * A map of 301 redirects in the form of original URL or path to destination URL or path.
     *
     * @return
     */
    public Map<String, String> getRedirects() {
        return redirects;
    }

    public void setRedirects(Map<String, String> redirects) {
        this.redirects = redirects;
    }

    /**
     * The meta tag value for generator. "Stallion" by default.
     * @return
     */
    public String getMetaGenerator() {
        return metaGenerator;
    }

    public void setMetaGenerator(String metaGenerator) {
        this.metaGenerator = metaGenerator;
    }

    /**
     * The path to the log file when file-based logging is on.
     *
     * @return
     */
    public String getLogFile() {
        return logFile;
    }

    public void setLogFile(String logFile) {
        this.logFile = logFile;
    }

    /**
     * If true, log to the console instead of file. This defaults to true in local mode.
     *
     * @return
     */
    public Boolean getLogToConsole() {
        return logToConsole;
    }

    public void setLogToConsole(Boolean logToConsole) {
        this.logToConsole = logToConsole;
    }

    public Boolean getLogToFile() {
        return logToFile;
    }

    public void setLogToFile(Boolean logToFile) {
        this.logToFile = logToFile;
    }

    /**
     * True if this is a developer running locally, false if this is running deployed on a server.
     *
     * If localMode is true, production jobs and tasks will not be run and logging will go to the console.
     *
     *
     * @return
     */
    public Boolean getLocalMode() {
        return localMode;
    }

    public void setLocalMode(Boolean localMode) {
        this.localMode = localMode;
    }

    public Boolean getLightweightMode() {
        return lightweightMode;
    }

    public Settings setLightweightMode(Boolean lightweightMode) {
        this.lightweightMode = lightweightMode;
        return this;
    }

    /**
     * A mapping of internal rewrites, from source path to destintion path.
     * @return
     */
    public Map<String, String> getRewrites() {
        return rewrites;
    }

    public void setRewrites(Map<String, String> rewrites) {
        this.rewrites = rewrites;
    }

    /**
     * A list of domains that content is also accesible at, and their mapping
     * to URL paths.
     *
     * @return
     */
    public List<SecondaryDomain> getSecondaryDomains() {
        return secondaryDomains;
    }

    public Settings setSecondaryDomains(List<SecondaryDomain> secondaryDomains) {
        this.secondaryDomains = secondaryDomains;
        return this;
    }

    public List<Map.Entry<Pattern, String>> getRewriteCompiledPatterns() {
        return rewriteCompiledPatterns;
    }

    public void setRewriteCompiledPatterns(List<Map.Entry<Pattern, String>> rewriteCompiledPatterns) {
        this.rewriteCompiledPatterns = rewriteCompiledPatterns;
    }

    /**
     * A list of arrays, each array has two values, a source regular expression and a destination path.
     * @return
     */
    public List<String[]> getRewritePatterns() {
        return rewritePatterns;
    }

    public void setRewritePatterns(List<String[]> rewritePatterns) {
        this.rewritePatterns = rewritePatterns;
    }

    /**
     * Configuration for integration with a cloud file storage, such as S3.
     *
     * @return
     */
    public CloudStorageSettings getCloudStorage() {
        return cloudStorage;
    }

    public void setCloudStorage(CloudStorageSettings cloudStorage) {
        this.cloudStorage = cloudStorage;
    }

    /**
     * Default CSS styles and colors that will be used in the default templates
     * for log in, password reset emails, etc.
     *
     * @return
     */
    public StyleSettings getStyles() {
        return styles;
    }

    public void setStyles(StyleSettings style) {
        this.styles = style;
    }

    /**
     * For deployed sites using stablehand, which node this is.
     *
     * @return
     */
    public Integer getNodeNumber() {
        return nodeNumber;
    }

    public Settings setNodeNumber(Integer nodeNumber) {
        this.nodeNumber = nodeNumber;
        return this;
    }

    /**
     * A random token used for preventing spam in form submissions and comments.
     *
     * @return
     */
    public String getAntiSpamSecret() {
        return antiSpamSecret;
    }

    public Settings setAntiSpamSecret(String antiSpamSecret) {
        this.antiSpamSecret = antiSpamSecret;
        return this;
    }

    /**
     * If true, the default form submission endpoint will be disabled.
     * @return
     */
    public Boolean getDisableFormSubmissions() {
        return disableFormSubmissions;
    }

    public Settings setDisableFormSubmissions(Boolean disableFormSubmissions) {
        this.disableFormSubmissions = disableFormSubmissions;
        return this;
    }

    /**
     * If there is a proxy server in front of Stallion, that proxy server will set
     * the IP address of the original requester and put it in a header. Add that header
     * name here so that Stallion can know the IP of the original requester.
     *
     * @return
     */
    public String getIpHeaderName() {
        return ipHeaderName;
    }

    public Settings setIpHeaderName(String ipHeaderName) {
        this.ipHeaderName = ipHeaderName;
        return this;
    }

    /**
     * Configuration for enabling users to give out OAuth access
     *
     * @return
     */
    public OAuthSettings getoAuth() {
        return oAuth;
    }

    public Settings setoAuth(OAuthSettings oAuth) {
        this.oAuth = oAuth;
        return this;
    }

    public Long getFilterCacheSize() {
        return filterCacheSize;
    }

    public Settings setFilterCacheSize(Long filterCacheSize) {
        this.filterCacheSize = filterCacheSize;
        return this;
    }

    public SecretsSettings getSecrets() {
        return secrets;
    }

    public Settings setSecrets(SecretsSettings secrets) {
        this.secrets = secrets;
        return this;
    }

    /**
     * Included in the templates for error pages.
     *
     * @return
     */
    public String getSupportEmail() {
        return supportEmail;
    }

    public Settings setSupportEmail(String supportEmail) {
        this.supportEmail = supportEmail;
        return this;
    }

    /*
    public List<PublishingConfig> getPublishing() {
    return publishing;
    }
        
        
    public Settings setPublishing(List publishing) {
    if (publishing == null) {
        this.publishing = null;
        return this;
    }
    if (publishing.size() == 0) {
        this.publishing = new ArrayList<>();
        return this;
    }
    this.publishing = new ArrayList<>();
    if (publishing.get(0) instanceof Map) {
        for(Object o: publishing) {
            PublishingConfig config = new PublishingConfig();
            Map<String, Object> m = (Map<String, Object>)o;
            for(Map.Entry<String, Object> e: m.entrySet()) {
                Object value = e.getValue();
                if (e.getKey().equals("basePort") && e.getValue() instanceof Long) {
                    value = Math.toIntExact((Long)value);
                }
                PropertyUtils.setProperty(config, e.getKey(), value);
            }
            this.publishing.add(config);
        }
    } else {
        this.publishing.addAll(publishing);
    }
    return this;
    }
    */

    public Long getAppCreatedMillis() {
        return appCreatedMillis;
    }

    public Settings setAppCreatedMillis(Long appCreatedMillis) {
        this.appCreatedMillis = appCreatedMillis;
        return this;
    }

    public List<AssetPreprocessorConfig> getAssetPreprocessors() {
        return assetPreprocessors;
    }

    public Settings setAssetPreprocessors(List assetPreProcessors) {
        this.assetPreprocessors = convertMapListToObjects(assetPreProcessors, AssetPreprocessorConfig.class);
        for (AssetPreprocessorConfig config : this.assetPreprocessors) {
            if (empty(config.getCommand()) || empty(config.getName()) || empty(config.getExtension())) {
                throw new ConfigException("Asset Pre-processors must have a valid command, name, and extension");
            }
        }
        return this;
    }

    protected List convertMapListToObjects(List maps, Class targetClass) {
        if (maps.size() == 0) {
            return maps;
        }
        if (targetClass.isAssignableFrom(maps.get(0).getClass())) {
            return maps;
        }
        List items = new ArrayList<>();
        for (Object o : maps) {
            Map<String, Object> map = (Map<String, Object>) o;
            try {
                Object instance = targetClass.newInstance();
                for (Map.Entry<String, Object> e : map.entrySet()) {
                    Object value = e.getValue();
                    if (e.getKey().equals("basePort") && e.getValue() instanceof Long) {
                        value = Math.toIntExact((Long) value);
                    }
                    PropertyUtils.setProperty(instance, e.getKey(), value);
                }
                items.add(instance);

            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return items;
    }

    public String getxFrameOptions() {
        return xFrameOptions;
    }

    public Settings setxFrameOptions(String xFrameOptions) {
        this.xFrameOptions = xFrameOptions;
        return this;
    }

    public String getNginxClientMaxBodySize() {
        return nginxClientMaxBodySize;
    }

    public Settings setNginxClientMaxBodySize(String nginxClientMaxBodySize) {
        this.nginxClientMaxBodySize = nginxClientMaxBodySize;
        return this;
    }

    public String getNginxProxyReadTimeout() {
        return nginxProxyReadTimeout;
    }

    public Settings setNginxProxyReadTimeout(String nginxProxyReadTimeout) {
        this.nginxProxyReadTimeout = nginxProxyReadTimeout;
        return this;
    }
}