org.ekrich.config.ConfigFactory.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sconfig_sjs1.0-RC2_2.13 Show documentation
Show all versions of sconfig_sjs1.0-RC2_2.13 Show documentation
Configuration library for Scala using HOCON files
The newest version!
/**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package org.ekrich.config
import org.ekrich.config.impl.ConfigImpl
import org.ekrich.config.impl.Parseable
import java.io.{File, Reader}
import java.net.URL
import java.{util => ju}
import java.util.Properties
import java.util.concurrent.Callable
/**
* Contains static methods for creating [[Config]] instances.
*
* See also [[ConfigValueFactory]] which contains static methods for
* converting Java values into a [[ConfigObject]]. You can then convert a
* [[ConfigObject]] into a [[Config]] with [[ConfigObject#toConfig ConfigObject.toConfig()]].
*
* The static methods with "load" in the name do some sort of higher-level
* operation potentially parsing multiple resources and resolving substitutions,
* while the ones with "parse" in the name just create a [[ConfigValue]]
* from a resource and nothing else.
*
* You can find an example app and library package
* overview which describes the big picture as shown in those
* examples.
*/
object ConfigFactory {
private val STRATEGY_PROPERTY_NAME = "config.strategy"
/**
* Loads an application's configuration from the given classpath resource or
* classpath resource basename, sandwiches it between default reference
* config and default overrides, and then resolves it. The classpath
* resource is "raw" (it should have no "/" prefix, and is not made relative
* to any package, so it's like `ClassLoader.getResource` not
* `Class.getResource`).
*
* Resources are loaded from the current thread's
* `Thread.getContextClassLoader`. In general, a library needs its
* configuration to come from the class loader used to load that library, so
* the proper "reference.conf" are present.
*
* The loaded object will already be resolved (substitutions have already
* been processed). As a result, if you add more fallbacks then they won't
* be seen by substitutions. Substitutions are the `\${foo.bar}` syntax. If
* you want to parse additional files or something then you need to use
* [[#load(config:org\.ekrich\.config\.Config)* load(Config)]].
*
* To load a standalone resource (without the default reference and default
* overrides), use
* [[#parseResourcesAnySyntax(resourceBasename:String)* parseResourcesAnySyntax(String)]]
* rather than this
* method. To load only the reference config use [[#defaultReference()* defaultReference()]]
* and to load only the overrides use [[#defaultOverrides()* defaultOverrides()]].
*
* @param resourceBasename
* name (optionally without extension) of a resource on classpath
* @return configuration for an application relative to context class loader
*/
def load(resourceBasename: String): Config =
load(
resourceBasename,
ConfigParseOptions.defaults,
ConfigResolveOptions.defaults
)
/**
* Like [[#load(resourceBasename:String)* ConfigFactory.load(String)]]
* but uses the supplied class loader instead of the current thread's
* context class loader.
*
* To load a standalone resource (without the default reference and default
* overrides), use
* [[#parseResourcesAnySyntax(loader:ClassLoader,resourceBasename:String)* parseResourcesAnySyntax(ClassLoader,String)]]
* rather than this method. To load only the reference config use
* [[#defaultReference(loader:ClassLoader)* defaultReference(ClassLoader)]] and to load only the overrides use
* [[#defaultOverrides(loader:ClassLoader)* defaultOverrides(ClassLoader)]].
*
* @param loader class loader to look for resources in
* @param resourceBasename basename (no .conf/.json/.properties suffix)
* @return configuration for an application relative to given class loader
*/
def load(loader: ClassLoader, resourceBasename: String): Config =
load(
resourceBasename,
ConfigParseOptions.defaults.setClassLoader(loader),
ConfigResolveOptions.defaults
)
/**
* Like [[#load(resourceBasename:String)* ConfigFactory.load(String)]]
* but allows you to specify parse and resolve options.
*
* @param resourceBasename
* the classpath resource name with optional extension
* @param parseOptions
* options to use when parsing the resource
* @param resolveOptions
* options to use when resolving the stack
* @return configuration for an application
*/
def load(
resourceBasename: String,
parseOptions: ConfigParseOptions,
resolveOptions: ConfigResolveOptions
): Config = {
val withLoader = ensureClassLoader(parseOptions, "load")
val appConfig =
ConfigFactory.parseResourcesAnySyntax(resourceBasename, withLoader)
load(withLoader.getClassLoader, appConfig, resolveOptions)
}
/**
* Like
* [[#load(resourceBasename:String,parseOptions:org\.ekrich\.config\.ConfigParseOptions,resolveOptions:org\.ekrich\.config\.ConfigResolveOptions)* load(String,ConfigParseOptions,ConfigResolveOptions)]]
* but has a class loader parameter that overrides any from the
* [[ConfigParseOptions]].
*
* @param loader
* class loader in which to find resources (overrides loader in
* parse options)
* @param resourceBasename
* the classpath resource name with optional extension
* @param parseOptions
* options to use when parsing the resource (class loader
* overridden)
* @param resolveOptions
* options to use when resolving the stack
* @return configuration for an application
*/
def load(
loader: ClassLoader,
resourceBasename: String,
parseOptions: ConfigParseOptions,
resolveOptions: ConfigResolveOptions
): Config =
load(resourceBasename, parseOptions.setClassLoader(loader), resolveOptions)
private def checkedContextClassLoader(methodName: String) = {
val loader = Thread.currentThread.getContextClassLoader
if (loader == null)
throw new ConfigException.BugOrBroken(
"Context class loader is not set for the current thread; " +
"if Thread.currentThread.getContextClassLoader returns null, you must pass a ClassLoader " +
"explicitly to ConfigFactory." + methodName
)
else loader
}
private def ensureClassLoader(
options: ConfigParseOptions,
methodName: String
) =
if (options.getClassLoader == null)
options.setClassLoader(checkedContextClassLoader(methodName))
else options
/**
* Assembles a standard configuration using a custom `Config`
* object rather than loading "application.conf". The `Config`
* object will be sandwiched between the default reference config and
* default overrides and then resolved.
*
* @param config
* the application's portion of the configuration
* @return resolved configuration with overrides and fallbacks added
*/
def load(config: Config): Config =
load(checkedContextClassLoader("load"), config)
/**
* Like [[#load(config:org\.ekrich\.config\.Config)* load(Config)]]
* but allows you to specify the class loader for looking up resources.
*
* @param loader
* the class loader to use to find resources
* @param config
* the application's portion of the configuration
* @return resolved configuration with overrides and fallbacks added
*/
def load(loader: ClassLoader, config: Config): Config =
load(loader, config, ConfigResolveOptions.defaults)
/**
* Like [[#load(config:org\.ekrich\.config\.Config)* load(Config)]]
* but allows you to specify [[ConfigResolveOptions]].
*
* @param config
* the application's portion of the configuration
* @param resolveOptions
* options for resolving the assembled config stack
* @return resolved configuration with overrides and fallbacks added
*/
def load(config: Config, resolveOptions: ConfigResolveOptions): Config =
load(checkedContextClassLoader("load"), config, resolveOptions)
/**
* Like
* [[#load(config:org\.ekrich\.config\.Config,resolveOptions:org\.ekrich\.config\.ConfigResolveOptions)* load(Config,ConfigResolveOptions)]]
* but allows you to specify a class loader other than the context class loader.
*
* @param loader
* class loader to use when looking up override and reference
* configs
* @param config
* the application's portion of the configuration
* @param resolveOptions
* options for resolving the assembled config stack
* @return resolved configuration with overrides and fallbacks added
*/
def load(
loader: ClassLoader,
config: Config,
resolveOptions: ConfigResolveOptions
): Config =
defaultOverrides(loader)
.withFallback(config)
.withFallback(defaultReference(loader))
.resolve(resolveOptions)
/**
* Loads a default configuration, equivalent to
* [[#load(config:org\.ekrich\.config\.Config)* load(Config)]]
* `ConfigFactory.load(defaultApplication()) in most cases. This
* configuration should be used by libraries and frameworks unless an
* application provides a different one.
*
* This method may return a cached singleton so will not see changes to
* system properties or config files. (Use [[#invalidateCaches invalidateCaches()]] to
* force it to reload.)
*
* @return configuration for an application
*/
def load(): Config = {
val loader = checkedContextClassLoader("load")
load(loader)
}
/**
* Like [[#load()* load()]] but allows specifying parse options.
*
* @param parseOptions
* Options for parsing resources
* @return configuration for an application
*/
def load(parseOptions: ConfigParseOptions): Config =
load(parseOptions, ConfigResolveOptions.defaults)
/**
* Like [[#load()* load()]] but allows specifying a class loader other than the
* thread's current context class loader.
*
* @param loader
* class loader for finding resources
* @return configuration for an application
*/
def load(loader: ClassLoader): Config = {
val withLoader =
ConfigParseOptions.defaults.setClassLoader(loader)
val updater = new Callable[Config] {
override def call(): Config =
load(loader, defaultApplication(withLoader))
}
ConfigImpl.computeCachedConfig(loader, "load", updater)
}
/**
* Like [[#load()* load()]] but allows specifying a class loader other than the
* thread's current context class loader and also specify parse options.
*
* @param loader
* class loader for finding resources (overrides any loader in parseOptions)
* @param parseOptions
* Options for parsing resources
* @return configuration for an application
*/
def load(loader: ClassLoader, parseOptions: ConfigParseOptions): Config =
load(parseOptions.setClassLoader(loader))
/**
* Like [[#load()* load()]] but allows specifying a class loader other than the
* thread's current context class loader and also specify resolve options.
*
* @param loader
* class loader for finding resources
* @param resolveOptions
* options for resolving the assembled config stack
* @return configuration for an application
*/
def load(loader: ClassLoader, resolveOptions: ConfigResolveOptions): Config =
load(loader, ConfigParseOptions.defaults, resolveOptions)
/**
* Like [[#load()* load()]] but allows specifying a class loader other than the
* thread's current context class loader, parse options, and resolve options.
*
* @param loader
* class loader for finding resources (overrides any loader in parseOptions)
* @param parseOptions
* Options for parsing resources
* @param resolveOptions
* options for resolving the assembled config stack
* @return configuration for an application
*/
def load(
loader: ClassLoader,
parseOptions: ConfigParseOptions,
resolveOptions: ConfigResolveOptions
): Config = {
val withLoader = ensureClassLoader(parseOptions, "load")
load(loader, defaultApplication(withLoader), resolveOptions)
}
/**
* Like [[#load()* load()]] but allows specifying parse options and resolve
* options.
*
* @param parseOptions
* Options for parsing resources
* @param resolveOptions
* options for resolving the assembled config stack
* @return configuration for an application
* @since 1.3.0
*/
def load(
parseOptions: ConfigParseOptions,
resolveOptions: ConfigResolveOptions
): Config = {
val withLoader = ensureClassLoader(parseOptions, "load")
load(defaultApplication(withLoader), resolveOptions)
}
/**
* Obtains the default reference configuration, which is currently created
* by merging all resources "reference.conf" found on the classpath and
* overriding the result with system properties. The returned reference
* configuration will already have substitutions resolved.
*
* Libraries and frameworks should ship with a "reference.conf" in their
* jar.
*
* The reference config must be looked up in the class loader that contains
* the libraries that you want to use with this config, so the
* "reference.conf" for each library can be found. Use
* [[#defaultReference(loader:ClassLoader)* defaultReference(ClassLoader)]] if the context class loader is not
* suitable.
*
* The [[#load()* load()]] methods merge this configuration for you
* automatically.
*
* Future versions may look for reference configuration in more places. It
* is not guaranteed that this method ''only'' looks at
* "reference.conf".
*
* @return the default reference config for context class loader
*/
def defaultReference(): Config =
defaultReference(checkedContextClassLoader("defaultReference"))
/**
* Like [[#defaultReference()* defaultReference()]] but allows you to specify a class loader
* to use rather than the current context class loader.
*
* @param loader class loader to look for resources in
* @return the default reference config for this class loader
*/
def defaultReference(loader: ClassLoader): Config =
ConfigImpl.defaultReference(loader)
/**
* Obtains the default override configuration, which currently consists of
* system properties. The returned override configuration will already have
* substitutions resolved.
*
* The [[#load()* load()]] methods merge this configuration for you
* automatically.
*
* Future versions may get overrides in more places. It is not guaranteed
* that this method only uses system properties.
*
* @return the default override configuration
*/
def defaultOverrides(): Config = systemProperties()
/**
* Like [[#defaultOverrides()* defaultOverrides()]] but allows you to specify a class loader
* to use rather than the current context class loader.
*
* @param loader class loader to look for resources in
* @return the default override configuration
*/
def defaultOverrides(loader: ClassLoader): Config = systemProperties()
/**
* Obtains the default application-specific configuration,
* which defaults to parsing `application.conf`,
* `application.json`, and
* `application.properties` on the classpath, but
* can also be rerouted using the `config.file`,
* `config.resource`, and `config.url`
* system properties.
*
* The no-arguments [[#load()* load()]] method automatically stacks the
* [[#defaultReference()* defaultReference()]],
* [[#defaultApplication()* defaultApplication()]],
* and [[#defaultOverrides()* defaultOverrides()]]
* configs. You would use `defaultApplication()`
* directly only if you're somehow customizing behavior by
* reimplementing `load()`.
*
* The configuration returned by
* `defaultApplication()` will not be resolved
* already, in contrast to `defaultReference()` and
* `defaultOverrides()`. This is because
* application.conf would normally be resolved ''after''
* merging with the reference and override configs.
*
* If the system properties `config.resource`,
* `config.file`, or `config.url` are set, then the
* classpath resource, file, or URL specified in those properties will be
* used rather than the default
* `application.{conf,json,properties]]` classpath resources.
* These system properties should not be set in code (after all, you can
* just parse whatever you want manually and then use
* [[#load(config:org\.ekrich\.config\.Config)* load(Config)]]
* if you don't want to use `application.conf`). The properties
* are intended for use by the person or script launching the application.
* For example someone might have a `production.conf` that
* include `application.conf` but then change a couple of values.
* When launching the app they could specify
* `-Dconfig.resource=production.conf` to get production mode.
*
* If no system properties are set to change the location of the default
* configuration, `defaultApplication()` is equivalent to
* `ConfigFactory.parseResources("application")`.
*
* @since 1.3.0
* @return the default application.conf or system-property-configured configuration
*/
def defaultApplication(): Config =
defaultApplication(ConfigParseOptions.defaults)
/**
* Like [[#defaultApplication()* defaultApplication()]] but allows you to specify a class loader
* to use rather than the current context class loader.
*
* @since 1.3.0
* @param loader class loader to look for resources in
* @return the default application configuration
*/
def defaultApplication(loader: ClassLoader): Config =
defaultApplication(ConfigParseOptions.defaults.setClassLoader(loader))
/**
* Like [[#defaultApplication()* defaultApplication()]] but allows you to specify parse options.
*
* @since 1.3.0
* @param options the options
* @return the default application configuration
*/
def defaultApplication(options: ConfigParseOptions): Config =
getConfigLoadingStrategy.parseApplicationConfig(
ensureClassLoader(options, "defaultApplication")
)
/**
* Reloads any cached configs, picking up changes to system properties for
* example. Because a [[Config]] is immutable, anyone with a reference
* to the old configs will still have the same outdated objects. However,
* new calls to [[#load()* load()]] or [[#defaultOverrides()* defaultOverrides()]]
* or [[#defaultReference()* defaultReference()]] may return a new object.
*
* This method is primarily intended for use in unit tests, for example,
* that may want to update a system property then confirm that it's used
* correctly. In many cases, use of this method may indicate there's a
* better way to set up your code.
*
* Caches may be reloaded immediately or lazily; once you call this method,
* the reload can occur at any time, even during the invalidation process.
* So FIRST make the changes you'd like the caches to notice, then SECOND
* call this method to invalidate caches. Don't expect that invalidating,
* making changes, then calling [[#load()* load()]], will work. Make changes
* before you invalidate.
*/
def invalidateCaches(): Unit = {
// We rely on this having the side effect that it drops all caches
ConfigImpl.reloadSystemPropertiesConfig()
ConfigImpl.reloadEnvVariablesConfig()
}
/**
* Gets an empty configuration. See also
* [[#empty(originDescription:String)* empty(String)]] to create an
* empty configuration with a description, which may improve user-visible
* error messages.
*
* @return an empty configuration
*/
def empty(): Config = empty(null)
/**
* Gets an empty configuration with a description to be used to create a
* [[ConfigOrigin]] for this `Config`. The description should
* be very short and say what the configuration is, like "default settings"
* or "foo settings" or something. (Presumably you will merge some actual
* settings into this empty config using [[Config.withFallback]], making
* the description more useful.)
*
* @param originDescription
* description of the config
* @return an empty configuration
*/
def empty(originDescription: String): Config =
ConfigImpl.emptyConfig(originDescription)
/**
* Gets a `Config` containing the system properties from
* `java.lang.System.getProperties()`, parsed and converted as with
* [[ConfigFactory$.parseProperties(properties:java\.util\.Properties)* parseProperties(Properties)]].
*
* This method can return a global immutable singleton, so it's preferred
* over parsing system properties yourself.
*
* [[#load()* ConfigFactory.load()]] will include the system properties as overrides already, as
* will [[#defaultReference()* defaultReference()]] and [[#defaultOverrides()* defaultOverrides()]].
*
* Because this returns a singleton, it will not notice changes to system
* properties made after the first time this method is called. Use
* [[#invalidateCaches invalidateCaches()]] to force the singleton to reload if you
* modify system properties.
*
* @return system properties parsed into a [[Config]]
*/
def systemProperties(): Config = ConfigImpl.systemPropertiesAsConfig
/**
* Gets a [[Config]] containing the system's environment variables.
* This method can return a global immutable singleton.
*
* Environment variables are used as fallbacks when resolving substitutions
* whether or not this object is included in the config being resolved, so
* you probably don't need to use this method for most purposes. It can be a
* nicer API for accessing environment variables than raw
* `java.lang.System.getenv(String)` though, since you can use methods
* such as [[Config.getInt]].
*
* @return system environment variables parsed into a `Config`
*/
def systemEnvironment(): Config = ConfigImpl.envVariablesAsConfig
/**
* Converts a Java `java.util.Properties` object to a
* [[ConfigObject]] using the rules documented in the
* [[https://github.com/ekrich/sconfig/blob/master/HOCON.md HOCON spec]]
* The keys in the `Properties` object are split on the
* period character '.' and treated as paths. The values will all end up as
* string values. If you have both "a=foo" and "a.b=bar" in your properties
* file, so "a" is both the object containing "b" and the string "foo", then
* the string value is dropped.
*
* If you want to have `System.getProperties()` as a
* ConfigObject, it's better to use the [[#systemProperties]] method
* which returns a cached global singleton.
*
* @param properties
* a Java Properties object
* @param options
* the parse options
* @return the parsed configuration
*/
def parseProperties(
properties: Properties,
options: ConfigParseOptions
): Config =
Parseable.newProperties(properties, options).parse().toConfig
/**
* Like
* [[#parseProperties(properties:java\.util\.Properties,options:org\.ekrich\.config\.ConfigParseOptions)* parseProperties(Properties, ConfigParseOptions)]]
* but uses default parse options.
*
* @param properties
* a Java Properties object
* @return the parsed configuration
*/
def parseProperties(properties: Properties): Config =
parseProperties(properties, ConfigParseOptions.defaults)
/**
* Parses a Reader into a Config instance. Does not call
* [[Config!.resolve()* Config.resolve()]] or merge the parsed stream with
* any other configuration; this method parses a single stream and
* does nothing else. It does process "include" statements in
* the parsed stream, and may end up doing other IO due to those
* statements.
*
* @param reader
* the reader to parse
* @param options
* parse options to control how the reader is interpreted
* @return the parsed configuration
* @throws ConfigException on IO or parse errors
*/
def parseReader(reader: Reader, options: ConfigParseOptions): Config =
Parseable.newReader(reader, options).parse().toConfig
/**
* Parses a reader into a Config instance as with
* [[#parseReader(reader:java\.io\.Reader,options:org\.ekrich\.config\.ConfigParseOptions)* parseReader(Reader, ConfigParseOptions)]]
* but always uses the default parse options.
*
* @param reader
* the reader to parse
* @return the parsed configuration
* @throws ConfigException on IO or parse errors
*/
def parseReader(reader: Reader): Config =
parseReader(reader, ConfigParseOptions.defaults)
/**
* Parses a URL into a Config instance. Does not call
* [[Config!.resolve()* Config.resolve()]] or merge the parsed stream with
* any other configuration; this method parses a single stream and
* does nothing else. It does process "include" statements in
* the parsed stream, and may end up doing other IO due to those
* statements.
*
* @param url
* the url to parse
* @param options
* parse options to control how the url is interpreted
* @return the parsed configuration
* @throws ConfigException on IO or parse errors
*/
def parseURL(url: URL, options: ConfigParseOptions): Config =
Parseable.newURL(url, options).parse().toConfig
/**
* Parses a url into a [[Config]] instance as with
* [[#parseURL(url:java\.net\.URL,options:org\.ekrich\.config\.ConfigParseOptions)* ConfigFactory.parseURL(URL,ConfigParseOptions)]]
* but always uses the default parse options.
*
* @param url
* the url to parse
* @return the parsed configuration
* @throws ConfigException on IO or parse errors
*/
def parseURL(url: URL): Config = parseURL(url, ConfigParseOptions.defaults)
/**
* Parses a file into a Config instance. Does not call
* [[Config!.resolve()* Config.resolve()]] or merge the file with any
* other configuration; this method parses a single file and does
* nothing else. It does process "include" statements in the
* parsed file, and may end up doing other IO due to those
* statements.
*
* @param file
* the file to parse
* @param options
* parse options to control how the file is interpreted
* @return the parsed configuration
* @throws ConfigException on IO or parse errors
*/
def parseFile(file: File, options: ConfigParseOptions): Config =
Parseable.newFile(file, options).parse().toConfig
/**
* Parses a file into a Config instance as with
* [[#parseFile(file:java\.io\.File,options:org\.ekrich\.config\.ConfigParseOptions)* parseFile(File,ConfigParseOptions)]]
* but always uses the default parse options.
*
* @param file
* the file to parse
* @return the parsed configuration
* @throws ConfigException on IO or parse errors
*/
def parseFile(file: File): Config =
parseFile(file, ConfigParseOptions.defaults)
/**
* Parses a file with a flexible extension. If the `fileBasename`
* already ends in a known extension, this method parses it according to
* that extension (the file's syntax must match its extension). If the
* `fileBasename` does not end in an extension, it parses files
* with all known extensions and merges whatever is found.
*
* In the current implementation, the extension ".conf" forces
* [[ConfigSyntax.CONF]], ".json" forces [[ConfigSyntax.JSON]], and
* ".properties" forces [[ConfigSyntax.PROPERTIES]]. When merging files,
* ".conf" falls back to ".json" falls back to ".properties".
*
* Future versions of the implementation may add additional syntaxes or
* additional extensions. However, the ordering (fallback priority) of the
* three current extensions will remain the same.
*
* If `options` forces a specific syntax, this method only parses
* files with an extension matching that syntax.
*
* If [[ConfigParseOptions#getAllowMissing]]
* is true, then no files have to exist; if false, then at least one file
* has to exist.
*
* @param fileBasename
* a filename with or without extension
* @param options
* parse options
* @return the parsed configuration
*/
def parseFileAnySyntax(
fileBasename: File,
options: ConfigParseOptions
): Config =
ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig
/**
* Like
* [[#parseFileAnySyntax(fileBasename:java\.io\.File,options:org\.ekrich\.config\.ConfigParseOptions)* parseFileAnySyntax(File,ConfigParseOptions)]]
* but always uses default parse options.
*
* @param fileBasename
* a filename with or without extension
* @return the parsed configuration
*/
def parseFileAnySyntax(fileBasename: File): Config =
parseFileAnySyntax(fileBasename, ConfigParseOptions.defaults)
/**
* Parses all resources on the classpath with the given name and merges them
* into a single [[Config]].
*
* If the resource name does not begin with a "/", it will have the supplied
* class's package added to it, in the same way as
* `java.lang.Class.getResource`.
*
* Duplicate resources with the same name are merged such that ones returned
* earlier from `ClassLoader.getResources` fall back to (have higher
* priority than) the ones returned later# This implies that resources
* earlier in the classpath override those later in the classpath when they
* configure the same setting# However, in practice real applications may
* not be consistent about classpath ordering, so be careful# It may be best
* to avoid assuming too much#
*
* @param klass
* `klass.getClassLoader()` will be used to load
* resources, and non-absolute resource names will have this
* class's package added
* @param resource
* resource to look up, relative to `klass`'s package
* or absolute starting with a "/"
* @param options
* parse options
* @return the parsed configuration
*/
def parseResources(
klass: Class[_],
resource: String,
options: ConfigParseOptions
): Config =
Parseable.newResources(klass, resource, options).parse().toConfig
/**
* Like
* [[#parseResources(klass:Class[_],resource:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResources(Class,String,ConfigParseOptions)]]
* but always uses default parse options.
*
* @param klass
* `klass.getClassLoader()` will be used to load
* resources, and non-absolute resource names will have this
* class's package added
* @param resource
* resource to look up, relative to `klass`'s package
* or absolute starting with a "/"
* @return the parsed configuration
*/
def parseResources(klass: Class[_], resource: String): Config =
parseResources(klass, resource, ConfigParseOptions.defaults)
/**
* Parses classpath resources with a flexible extension. In general, this
* method has the same behavior as
* [[#parseFileAnySyntax(fileBasename:java\.io\.File,options:org\.ekrich\.config\.ConfigParseOptions)* parseFileAnySyntax(File,ConfigParseOptions)]]
* but for classpath resources instead, as in [[#parseResources(resource:String)* parseResources(String)]].
*
* There is a thorny problem with this method, which is that
* `java.lang.ClassLoader.getResources` must be called separately for
* each possible extension. The implementation ends up with separate lists
* of resources called "basename.conf" and "basename.json" for example. As a
* result, the ideal ordering between two files with different extensions is
* unknown; there is no way to figure out how to merge the two lists in
* classpath order. To keep it simple, the lists are simply concatenated,
* with the same syntax priorities as
* [[#parseFileAnySyntax(fileBasename:java\.io\.File,options:org\.ekrich\.config\.ConfigParseOptions)* parseFileAnySyntax(File,ConfigParseOptions)]]
* - all ".conf" resources are ahead of all ".json" resources which are
* ahead of all ".properties" resources.
*
* @param klass
* class which determines the `ClassLoader` and the
* package for relative resource names
* @param resourceBasename
* a resource name as in `java.lang.Class.getResource`,
* with or without extension
* @param options
* parse options (class loader is ignored in favor of the one
* from klass)
* @return the parsed configuration
*/
def parseResourcesAnySyntax(
klass: Class[_],
resourceBasename: String,
options: ConfigParseOptions
): Config =
ConfigImpl
.parseResourcesAnySyntax(klass, resourceBasename, options)
.toConfig
/**
* Like
* [[#parseResourcesAnySyntax(klass:Class[_],resourceBasename:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResourcesAnySyntax(Class,String,ConfigParseOptions)]]
* but always uses default parse options.
*
* @param klass
* `klass.getClassLoader()` will be used to load
* resources, and non-absolute resource names will have this
* class's package added
* @param resourceBasename
* a resource name as in `java.lang.Class.getResource`,
* with or without extension
* @return the parsed configuration
*/
def parseResourcesAnySyntax(
klass: Class[_],
resourceBasename: String
): Config =
parseResourcesAnySyntax(
klass,
resourceBasename,
ConfigParseOptions.defaults
)
/**
* Parses all resources on the classpath with the given name and merges them
* into a single `Config`.
*
* This works like `java.lang.ClassLoader.getResource`, not like
* `java.lang.Class.getResource`, so the name never begins with a
* slash.
*
* See
* [[#parseResources(klass:Class[_],resource:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResources(Class,String,ConfigParseOptions)]]
* for full details.
*
* @param loader
* will be used to load resources by setting this loader on the
* provided options
* @param resource
* resource to look up
* @param options
* parse options (class loader is ignored)
* @return the parsed configuration
*/
def parseResources(
loader: ClassLoader,
resource: String,
options: ConfigParseOptions
): Config =
parseResources(resource, options.setClassLoader(loader))
/**
* Like
* [[#parseResources(loader:ClassLoader,resource:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResources(ClassLoader,String,ConfigParseOptions)]]
* but always uses default parse options.
*
* @param loader
* will be used to load resources
* @param resource
* resource to look up in the loader
* @return the parsed configuration
*/
def parseResources(loader: ClassLoader, resource: String): Config =
parseResources(loader, resource, ConfigParseOptions.defaults)
/**
* Parses classpath resources with a flexible extension. In general, this
* method has the same behavior as
* [[#parseFileAnySyntax(fileBasename:java\.io\.File,options:org\.ekrich\.config\.ConfigParseOptions)* parseFileAnySyntax(File,ConfigParseOptions)]]
* but for classpath resources instead, as in
* [[#parseResources(loader:ClassLoader,resource:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResources(ClassLoader,String,ConfigParseOptions)]].
*
* [[#parseResourcesAnySyntax(klass:Class[_],resourceBasename:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResourcesAnySyntax(Class,String,ConfigParseOptions)]]
* differs in the syntax for the resource name, but it is useful to review for
* some details and caveats on this method.
*
* @param loader
* class loader to look up resources in, will be set on options
* @param resourceBasename
* a resource name as in
* `java.lang.ClassLoader.getResource`, with or without
* extension
* @param options
* parse options (class loader ignored)
* @return the parsed configuration
*/
def parseResourcesAnySyntax(
loader: ClassLoader,
resourceBasename: String,
options: ConfigParseOptions
): Config =
ConfigImpl
.parseResourcesAnySyntax(resourceBasename, options.setClassLoader(loader))
.toConfig
/**
* Like
* [[#parseResourcesAnySyntax(loader:ClassLoader,resourceBasename:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)]]
* but always uses default parse options.
*
* @param loader
* will be used to load resources
* @param resourceBasename
* a resource name as in
* `java.lang.ClassLoader.getResource`, with or without
* extension
* @return the parsed configuration
*/
def parseResourcesAnySyntax(
loader: ClassLoader,
resourceBasename: String
): Config =
parseResourcesAnySyntax(
loader,
resourceBasename,
ConfigParseOptions.defaults
)
/**
* Like
* [[#parseResources(loader:ClassLoader,resource:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResources(ClassLoader,String,ConfigParseOptions)]]
* but uses thread's current context class loader if none is set in the
* ConfigParseOptions.
*
* @param resource the resource name
* @param options parse options
* @return the parsed configuration
*/
def parseResources(resource: String, options: ConfigParseOptions): Config = {
val withLoader =
ensureClassLoader(options, "parseResources")
Parseable.newResources(resource, withLoader).parse().toConfig
}
/**
* Like
* [[#parseResources(loader:ClassLoader,resource:String)* parseResources(ClassLoader,String)]]
* but uses thread's current context class loader.
*
* @param resource the resource name
* @return the parsed configuration
*/
def parseResources(resource: String): Config =
parseResources(resource, ConfigParseOptions.defaults)
/**
* Like
* [[#parseResourcesAnySyntax(loader:ClassLoader,resourceBasename:String,options:org\.ekrich\.config\.ConfigParseOptions)* parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)]]
* but uses thread's current context class loader.
*
* @param resourceBasename the resource basename (no file type suffix)
* @param options parse options
* @return the parsed configuration
*/
def parseResourcesAnySyntax(
resourceBasename: String,
options: ConfigParseOptions
): Config =
ConfigImpl
.parseResourcesAnySyntax(resourceBasename, options)
.toConfig
/**
* Like
* [[#parseResourcesAnySyntax(loader:ClassLoader,resourceBasename:String)* parseResourcesAnySyntax(ClassLoader,String)]]
* but uses thread's current context class loader.
*
* @param resourceBasename the resource basename (no file type suffix)
* @return the parsed configuration
*/
def parseResourcesAnySyntax(resourceBasename: String): Config =
parseResourcesAnySyntax(resourceBasename, ConfigParseOptions.defaults)
/**
* Parses a string (which should be valid HOCON or JSON by default, or
* the syntax specified in the options otherwise).
*
* @param s string to parse
* @param options parse options
* @return the parsed configuration
*/
def parseString(s: String, options: ConfigParseOptions): Config =
Parseable.newString(s, options).parse().toConfig
/**
* Parses a string (which should be valid HOCON or JSON).
*
* @param s string to parse
* @return the parsed configuration
*/
def parseString(s: String): Config =
parseString(s, ConfigParseOptions.defaults)
/**
* Creates a [[Config]] based on a `java.util.Map` from paths to
* plain Java values. Similar to
* [[ConfigValueFactory$.fromMap(values:java\.util\.Map[String,_],originDescription:String)* ConfigValueFactory.fromMap(Map,String)]],
* except the keys in the map are path expressions, rather than keys; and
* correspondingly it returns a [[Config]] instead of a [[ConfigObject]].
* This is more convenient if you are writing literal maps in code, and less
* convenient if you are getting your maps from some data source such as a
* parser.
*
* An exception will be thrown (and it is a bug in the caller of the method)
* if a path is both an object and a value, for example if you had both
* "a=foo" and "a.b=bar", then "a" is both the string "foo" and the parent
* object of "b". The caller of this method should ensure that doesn't
* happen.
*
* @param values map from paths to plain Java objects
* @param originDescription
* description of what this map represents, like a filename, or
* "default settings" (origin description is used in error
* messages)
* @return the map converted to a [[Config]]
*/
def parseMap(values: ju.Map[String, _], originDescription: String): Config =
ConfigImpl.fromPathMap(values, originDescription).toConfig
/**
* See the other overload of
* [[ConfigFactory$.parseMap(values:java\.util\.Map[String,_],originDescription:String)* ConfigFactory.parseMap(Map,String)]]
* for details, this one just uses a default origin description.
*
* @param values map from paths to plain Java values
* @return the map converted to a [[Config]]
*/
def parseMap(values: ju.Map[String, _]): Config = parseMap(values, null)
private def getConfigLoadingStrategy = {
val className =
System.getProperties.getProperty(STRATEGY_PROPERTY_NAME)
if (className != null) {
try {
classOf[ConfigLoadingStrategy].cast(
Class.forName(className).getConstructor().newInstance()
)
} catch {
case e: Throwable =>
throw new ConfigException.BugOrBroken(
"Failed to load strategy: " + className,
e
)
}
} else {
new DefaultConfigLoadingStrategy()
}
}
}
final class ConfigFactory private () {}