mirror of
https://github.com/lightbend/config.git
synced 2025-01-15 23:01:05 +08:00
Overhaul ConfigFactory API and ConfigFactory.load().
Update all docs and tests. Add example library and application.
This commit is contained in:
parent
e6ee3d6232
commit
9aae9e2a80
96
HOCON.md
96
HOCON.md
@ -918,61 +918,40 @@ to access these strings that conflict with objects.
|
||||
The usual rule in HOCON would be that the later assignment in the
|
||||
file wins, rather than "object wins"; but implementing that for
|
||||
Java properties would require implementing a custom Java
|
||||
properties parser, which is surely not worth it.
|
||||
properties parser, which is surely not worth it and wouldn't help
|
||||
with system properties anyway.
|
||||
|
||||
### Root paths
|
||||
### Conventional configuration files for JVM apps
|
||||
|
||||
By convention, a given application or library has a "root path."
|
||||
Most commonly the root path has a single path element - "akka" for
|
||||
example. But it could have multiple.
|
||||
By convention, JVM apps have two parts to their configuration:
|
||||
|
||||
Conventional config file names and property names are derived from
|
||||
the root path.
|
||||
- the _reference_ config is made up of all resources named
|
||||
`reference.conf` on the classpath, merged in the order they
|
||||
are returned by `ClassLoader.getResources()`; also, system
|
||||
property overrides are applied.
|
||||
- the _application_ config can be loaded from anywhere an
|
||||
application likes, but by default if the application doesn't
|
||||
provide a config it would be loaded from files
|
||||
`application.{conf,json,properties}` on the classpath and
|
||||
then system property overrides are applied.
|
||||
- a single JVM may have multiple application configs.
|
||||
|
||||
If an API looks like `load(rootPath)` then it would return an
|
||||
object conceptually "at" the root path, not an object containing
|
||||
the root path.
|
||||
The reference config should be merged and resolved first, and
|
||||
shared among all application configs. Substitutions in the
|
||||
reference config are not affected by any application configs,
|
||||
because the reference config should be resolved by itself.
|
||||
|
||||
### Conventional configuration file names for JVM apps
|
||||
|
||||
To get config file names, join the elements of the root path with
|
||||
a hyphen, then add appropriate filename extensions.
|
||||
|
||||
If the root path is `foo.bar` (two elements, `foo` and `bar`),
|
||||
then the configuration files should be searched for under the
|
||||
following resource names on the classpath:
|
||||
|
||||
- /foo-bar.conf
|
||||
- /foo-bar.json
|
||||
- /foo-bar.properties
|
||||
- /foo-bar-reference.conf
|
||||
- /foo-bar-reference.json
|
||||
- /foo-bar-reference.properties
|
||||
|
||||
The .json and .properties files are examples, different
|
||||
implementations may support different file types. The "reference"
|
||||
files are intended to contain defaults and be shipped with the
|
||||
library or application being configured.
|
||||
|
||||
Note that the configuration files are absolute resource paths, not
|
||||
relative to the package. So you would call
|
||||
`klass.getResource("/foo-bar.conf")` not
|
||||
`klass.getResource("foo-bar.conf")`.
|
||||
The application config should then be loaded, have the reference
|
||||
config added as a fallback, and have substitutions resolved. This
|
||||
means the application config can refer to the reference config in
|
||||
its substitutions.
|
||||
|
||||
### Conventional override by system properties
|
||||
|
||||
For an application's config, Java System properties _override_
|
||||
HOCON found in the configuration file. This supports specifying
|
||||
settings found in the configuration file. This supports specifying
|
||||
config options on the command line.
|
||||
|
||||
Those system properties which begin with an application's root
|
||||
path should override the configuration for that application.
|
||||
|
||||
For example, say your config is for root path "akka" then your
|
||||
config key "foo" would go with `-Dakka.foo=10`. When loading your
|
||||
config, any system properties starting with `akka.` would be
|
||||
merged into the config.
|
||||
|
||||
### Substitution fallback to system properties
|
||||
|
||||
Recall that if a substitution is not present (not even set to
|
||||
@ -980,30 +959,11 @@ Recall that if a substitution is not present (not even set to
|
||||
for it from external sources. One such source could be Java system
|
||||
properties.
|
||||
|
||||
To find a value for substitution, Java applications should look at
|
||||
system properties directly, without the root path namespace.
|
||||
Remember that namespaced system properties were already used as
|
||||
overrides.
|
||||
|
||||
`${user.home}` would first look for a `user.home` in the
|
||||
configuration tree (which has a scoped system property like
|
||||
`akka.user.home` merged in!).
|
||||
|
||||
If no value for `${user.home}` exists in the configuration, the
|
||||
implementation would look at system property `user.home` without
|
||||
the `akka.` prefix.
|
||||
|
||||
The unprefixed system properties are _not_ merged in to the
|
||||
configuration tree; if you iterate over your configuration, they
|
||||
should not be in there. They are only used as a fallback when
|
||||
evaluating substitutions.
|
||||
|
||||
The effect is to allow using generic system properties like
|
||||
`user.home` and also to allow overriding those per-app.
|
||||
So if someone wants to set their home directory for _all_ apps,
|
||||
they set the `user.home` system property. If they then want to
|
||||
force a particular home directory only for Akka, they could set
|
||||
`akka.user.home` instead.
|
||||
If you merge system properties in to an application's
|
||||
configuration as overrides, then falling back to them for
|
||||
substitutions won't add anything - they would already be found in
|
||||
the configuration. But if you don't merge them in as overrides
|
||||
then you could fall back to them.
|
||||
|
||||
### Substitution fallback to environment variables
|
||||
|
||||
|
55
README.md
55
README.md
@ -49,24 +49,42 @@ and warrant that you have the legal authority to do so.
|
||||
|
||||
## API Example
|
||||
|
||||
ConfigRoot root = Config.load("myapp")
|
||||
int bar1 = conf.getInt("foo.bar")
|
||||
Config foo = conf.getConfig("foo")
|
||||
int bar2 = obj.getInt("bar")
|
||||
Config conf = ConfigFactory.load();
|
||||
int bar1 = conf.getInt("foo.bar");
|
||||
Config foo = conf.getConfig("foo");
|
||||
int bar2 = obj.getInt("bar");
|
||||
|
||||
## Longer Examples
|
||||
|
||||
See the examples in the `examples/` directory.
|
||||
|
||||
You can run these from the sbt console with the commands `project
|
||||
simple-app` and then `run`.
|
||||
|
||||
## Standard behavior
|
||||
|
||||
You can load any files and merge them in any order, but the
|
||||
convenience method `Config.load()` loads the following
|
||||
The convenience method `ConfigFactory.load()` loads the following
|
||||
(first-listed are higher priority):
|
||||
|
||||
- `myapp.*` system properties
|
||||
- `myapp.conf` (these files are all from classpath)
|
||||
- `myapp.json`
|
||||
- `myapp.properties`
|
||||
- `myapp-reference.conf`
|
||||
- `myapp-reference.json`
|
||||
- `myapp-reference.properties`
|
||||
- system properties
|
||||
- `application.conf` (all resources on classpath with this name)
|
||||
- `application.json` (all resources on classpath with this name)
|
||||
- `application.properties` (all resources on classpath with this
|
||||
name)
|
||||
- `reference.conf` (all resources on classpath with this name)
|
||||
|
||||
The idea is that libraries and frameworks should ship with a
|
||||
`reference.conf` in their jar. Applications should provide an
|
||||
`application.conf`, or if they want to create multiple
|
||||
configurations in a single JVM, they could use
|
||||
`ConfigFactory.load("myapp")` to load their own `myapp.conf`.
|
||||
|
||||
Libraries and frameworks should default to `ConfigFactory.load()`
|
||||
if the application does not provide a custom `Config`
|
||||
object. Libraries and frameworks should also allow the application
|
||||
to provide a custom `Config` object to be used instead of the
|
||||
default, in case the application needs multiple configurations in
|
||||
one JVM or wants to load extra config files from somewhere.
|
||||
|
||||
## JSON Superset
|
||||
|
||||
@ -238,17 +256,6 @@ Here are some features that might be nice to add.
|
||||
(Note that regular `=` already merges object values, to avoid
|
||||
object merge you have to first set the object to a non-object
|
||||
such as null, then set a new object.)
|
||||
- "application.conf": normally there is no "global"
|
||||
configuration, each application does its own
|
||||
`Config.load("myapp")`. However, it might be nice if you could
|
||||
put all your config for your app and libraries you use in a
|
||||
single file. This could be called "application.conf" for
|
||||
example. `Config.load("myapp")` would load "application.conf"
|
||||
and merge in the `"myapp"` object from "application.conf",
|
||||
so if "application.conf" contained: `myapp { foo=3 }` then
|
||||
the key `foo` would be set in the result of
|
||||
`Config.load("myapp")`. Apps could then put all their config
|
||||
in "application.conf", if desired.
|
||||
- "delete": allow deleting a field, which is slightly different
|
||||
from setting it to null (deletion allows fallback to values
|
||||
in system properties and the environment, for example).
|
||||
|
@ -27,94 +27,233 @@ import com.typesafe.config.impl.Parseable;
|
||||
* from a resource and nothing else.
|
||||
*/
|
||||
public final class ConfigFactory {
|
||||
private ConfigFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a configuration for the given root path in a "standard" way.
|
||||
* Oversimplified, if your root path is foo.bar then this will load files
|
||||
* from the classpath: foo-bar.conf, foo-bar.json, foo-bar.properties,
|
||||
* foo-bar-reference.conf, foo-bar-reference.json,
|
||||
* foo-bar-reference.properties. It will override all those files with any
|
||||
* system properties that begin with "foo.bar.", as well.
|
||||
*
|
||||
* The root path should be a path expression, usually just a single short
|
||||
* word, that scopes the package being configured; typically it's the
|
||||
* package name or something similar. System properties overriding values in
|
||||
* the configuration will have to be prefixed with the root path. The root
|
||||
* path may have periods in it if you like but other punctuation or
|
||||
* whitespace will probably cause you headaches. Example root paths: "akka",
|
||||
* "sbt", "jsoup", "heroku", "mongo", etc.
|
||||
* 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 {@link ClassLoader#getResource} not
|
||||
* {@link Class#getResource}).
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* loadWithoutResolving().
|
||||
* {@link #load(Config)}.
|
||||
*
|
||||
* @param rootPath
|
||||
* the configuration "domain"
|
||||
* @return configuration for the requested root path
|
||||
* @param resourceBasename
|
||||
* name (optionally without extension) of a resource on classpath
|
||||
* @return configuration for an application
|
||||
*/
|
||||
public static Config load(String rootPath) {
|
||||
return loadWithoutResolving(rootPath).resolve();
|
||||
}
|
||||
|
||||
public static Config load(String rootPath,
|
||||
ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
|
||||
return loadWithoutResolving(rootPath, parseOptions).resolve(
|
||||
resolveOptions);
|
||||
public static Config load(String resourceBasename) {
|
||||
return load(resourceBasename, ConfigParseOptions.defaults(),
|
||||
ConfigResolveOptions.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Like load() but does not resolve the config, so you can go ahead and add
|
||||
* more fallbacks and stuff and have them seen by substitutions when you do
|
||||
* call {@link Config#resolve()}.
|
||||
* Like {@link #load(String)} but allows you to specify parse and resolve
|
||||
* options.
|
||||
*
|
||||
* @param rootPath
|
||||
* @return configuration for the requested root path
|
||||
* <p>
|
||||
* To be aware of: using
|
||||
* {@link ConfigResolveOptions#setUseSystemProperties(boolean)
|
||||
* setUseSystemProperties(false)} with this method has no meaningful effect,
|
||||
* because the system properties are merged into the config as overrides
|
||||
* anyway. <code>setUseSystemProperties</code> affects whether to fall back
|
||||
* to system properties when they are not found in the config, but with
|
||||
* <code>load()</code>, they will be in the config.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static Config loadWithoutResolving(String rootPath) {
|
||||
return loadWithoutResolving(rootPath, ConfigParseOptions.defaults());
|
||||
public static Config load(String resourceBasename, ConfigParseOptions parseOptions,
|
||||
ConfigResolveOptions resolveOptions) {
|
||||
Config appConfig = ConfigFactory.parseResourcesAnySyntax(ConfigFactory.class, "/"
|
||||
+ resourceBasename, parseOptions);
|
||||
return load(appConfig, resolveOptions);
|
||||
}
|
||||
|
||||
public static Config loadWithoutResolving(String rootPath,
|
||||
ConfigParseOptions options) {
|
||||
Config system = ConfigImpl.systemPropertiesWithPrefix(rootPath);
|
||||
|
||||
Config mainFiles = parseResourcesForPath(rootPath, options);
|
||||
Config referenceFiles = parseResourcesForPath(rootPath + ".reference",
|
||||
options);
|
||||
|
||||
return system.withFallback(mainFiles).withFallback(referenceFiles);
|
||||
/**
|
||||
* Assembles a standard configuration using a custom <code>Config</code>
|
||||
* object rather than loading "application.conf". The <code>Config</code>
|
||||
* 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
|
||||
*/
|
||||
public static Config load(Config config) {
|
||||
return load(config, ConfigResolveOptions.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #load(Config)} but allows you to specify
|
||||
* {@link ConfigResolveOptions}.
|
||||
*
|
||||
* <p>
|
||||
* To be aware of: using
|
||||
* {@link ConfigResolveOptions#setUseSystemProperties(boolean)
|
||||
* setUseSystemProperties(false)} with this method has no meaningful effect,
|
||||
* because the system properties are merged into the config as overrides
|
||||
* anyway. <code>setUseSystemProperties</code> affects whether to fall back
|
||||
* to system properties when they are not found in the config, but with
|
||||
* <code>load()</code>, they will be in the config.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static Config load(Config config, ConfigResolveOptions resolveOptions) {
|
||||
return defaultOverrides().withFallback(config).withFallback(defaultReference())
|
||||
.resolve(resolveOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a default configuration, equivalent to {@link #load(String)
|
||||
* load("application")}. This configuration should be used by libraries and
|
||||
* frameworks unless an application provides a different one.
|
||||
*
|
||||
* @return configuration for an application
|
||||
*/
|
||||
public static Config load() {
|
||||
return load("application");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Libraries and frameworks should ship with a "reference.conf" in their
|
||||
* jar.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #load()} methods merge this configuration for you
|
||||
* automatically.
|
||||
*
|
||||
* <p>
|
||||
* Future versions may look for reference configuration in more places. It
|
||||
* is not guaranteed that this method <em>only</em> looks at
|
||||
* "reference.conf".
|
||||
*
|
||||
* @return the default reference config
|
||||
*/
|
||||
public static Config defaultReference() {
|
||||
return ConfigImpl.defaultReference();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the default override configuration, which currently consists of
|
||||
* system properties. The returned override configuration will already have
|
||||
* substitutions resolved.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #load()} methods merge this configuration for you
|
||||
* automatically.
|
||||
*
|
||||
* <p>
|
||||
* Future versions may get overrides in more places. It is not guaranteed
|
||||
* that this method <em>only</em> uses system properties.
|
||||
*
|
||||
* @return the default override configuration
|
||||
*/
|
||||
public static Config defaultOverrides() {
|
||||
return systemProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an empty configuration. See also {@link #empty(String)} to create an
|
||||
* empty configuration with a description, which may improve user-visible
|
||||
* error messages.
|
||||
*
|
||||
* @return an empty configuration
|
||||
*/
|
||||
public static Config empty() {
|
||||
return empty(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an empty configuration with a description to be used to create a
|
||||
* {@link ConfigOrigin} for this <code>Config</code>. 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 {@link Config#withFallback}, making
|
||||
* the description more useful.)
|
||||
*
|
||||
* @param originDescription
|
||||
* description of the config
|
||||
* @return an empty configuration
|
||||
*/
|
||||
public static Config empty(String originDescription) {
|
||||
return ConfigImpl.emptyConfig(originDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <code>Config</code> containing the system properties from
|
||||
* {@link java.lang.System#getProperties()}, parsed and converted as with
|
||||
* {@link #parseProperties}. This method can return a global immutable
|
||||
* singleton, so it's preferred over parsing system properties yourself.
|
||||
*
|
||||
* <p>
|
||||
* {@link #load} will include the system properties as overrides already, as
|
||||
* will {@link #defaultReference} and {@link #defaultOverrides}.
|
||||
*
|
||||
* <p>
|
||||
* Because this returns a singleton, it will not notice changes to system
|
||||
* properties made after the first time this method is called.
|
||||
*
|
||||
* @return system properties parsed into a <code>Config</code>
|
||||
*/
|
||||
public static Config systemProperties() {
|
||||
return ConfigImpl.systemPropertiesAsConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <code>Config</code> containing the system's environment variables.
|
||||
* This method can return a global immutable singleton.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* {@link java.lang.System#getenv(String)} though, since you can use methods
|
||||
* such as {@link Config#getInt}.
|
||||
*
|
||||
* @return system environment variables parsed into a <code>Config</code>
|
||||
*/
|
||||
public static Config systemEnvironment() {
|
||||
return ConfigImpl.envVariablesAsConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Java Properties object to a ConfigObject using the rules
|
||||
* documented in https://github.com/havocp/config/blob/master/HOCON.md 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.
|
||||
* Converts a Java {@link java.util.Properties} object to a
|
||||
* {@link ConfigObject} using the rules documented in the <a
|
||||
* href="https://github.com/havocp/config/blob/master/HOCON.md">HOCON
|
||||
* spec</a>. The keys in the <code>Properties</code> 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 get System.getProperties() as a ConfigObject, it's better
|
||||
* to use the systemProperties() or systemPropertiesRoot() methods. Those
|
||||
* methods are able to use a cached global singleton ConfigObject for the
|
||||
* system properties.
|
||||
* <p>
|
||||
* If you want to have <code>System.getProperties()</code> as a
|
||||
* ConfigObject, it's better to use the {@link #systemProperties()} method
|
||||
* which returns a cached global singleton.
|
||||
*
|
||||
* @param properties
|
||||
* a Java Properties object
|
||||
@ -126,18 +265,34 @@ public final class ConfigFactory {
|
||||
return Parseable.newProperties(properties, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parseProperties(Properties properties) {
|
||||
return parseProperties(properties, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
public static Config parseReader(Reader reader, ConfigParseOptions options) {
|
||||
return Parseable.newReader(reader, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parseReader(Reader reader) {
|
||||
return parseReader(reader, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
public static Config parseURL(URL url, ConfigParseOptions options) {
|
||||
return Parseable.newURL(url, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parseURL(URL url) {
|
||||
return parseURL(url, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
public static Config parseFile(File file, ConfigParseOptions options) {
|
||||
return Parseable.newFile(file, options).parse().toConfig();
|
||||
}
|
||||
|
||||
public static Config parseFile(File file) {
|
||||
return parseFile(file, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a file with a flexible extension. If the <code>fileBasename</code>
|
||||
* already ends in a known extension, this method parses it according to
|
||||
@ -176,6 +331,10 @@ public final class ConfigFactory {
|
||||
return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig();
|
||||
}
|
||||
|
||||
public static Config parseFileAnySyntax(File fileBasename) {
|
||||
return parseFileAnySyntax(fileBasename, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all resources on the classpath with the given name and merges them
|
||||
* into a single <code>Config</code>.
|
||||
@ -211,6 +370,10 @@ public final class ConfigFactory {
|
||||
.toConfig();
|
||||
}
|
||||
|
||||
public static Config parseResources(Class<?> klass, String resource) {
|
||||
return parseResources(klass, resource, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses classpath resources with a flexible extension. In general, this
|
||||
* method has the same behavior as
|
||||
@ -246,24 +409,16 @@ public final class ConfigFactory {
|
||||
options).toConfig();
|
||||
}
|
||||
|
||||
public static Config parseResourcesAnySyntax(Class<?> klass, String resourceBasename) {
|
||||
return parseResourcesAnySyntax(klass, resourceBasename, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
public static Config parseString(String s, ConfigParseOptions options) {
|
||||
return Parseable.newString(s, options).parse().toConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses classpath resources corresponding to this path expression.
|
||||
* Essentially if the path is "foo.bar" then the resources are
|
||||
* "/foo-bar.conf", "/foo-bar.json", and "/foo-bar.properties". If more than
|
||||
* one of those exists, they are merged.
|
||||
*
|
||||
* @param rootPath
|
||||
* @param options
|
||||
* @return the parsed configuration
|
||||
*/
|
||||
public static Config parseResourcesForPath(String rootPath,
|
||||
ConfigParseOptions options) {
|
||||
// null originDescription is allowed in parseResourcesForPath
|
||||
return ConfigImpl.parseResourcesForPath(rootPath, options).toConfig();
|
||||
public static Config parseString(String s) {
|
||||
return parseString(s, ConfigParseOptions.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,9 @@ import com.typesafe.config.impl.ConfigImpl;
|
||||
* data structures.
|
||||
*/
|
||||
public final class ConfigValueFactory {
|
||||
private ConfigValueFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ConfigValue from a plain Java boxed value, which may be a
|
||||
* Boolean, Number, String, Map, Iterable, or null. A Map must be a Map from
|
||||
@ -117,7 +120,7 @@ public final class ConfigValueFactory {
|
||||
* <p>
|
||||
* See also {@link ConfigFactory#parseMap(Map)} which interprets the keys in
|
||||
* the map as path expressions.
|
||||
*
|
||||
*
|
||||
* @param values
|
||||
* @return a new {@link ConfigObject}
|
||||
*/
|
||||
|
@ -87,32 +87,6 @@ public class ConfigImpl {
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static String makeResourceBasename(Path path) {
|
||||
StringBuilder sb = new StringBuilder("/");
|
||||
String next = path.first();
|
||||
Path remaining = path.remainder();
|
||||
while (next != null) {
|
||||
sb.append(next);
|
||||
sb.append('-');
|
||||
|
||||
if (remaining == null)
|
||||
break;
|
||||
|
||||
next = remaining.first();
|
||||
remaining = remaining.remainder();
|
||||
}
|
||||
sb.setLength(sb.length() - 1); // chop extra hyphen
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static ConfigObject parseResourcesForPath(String expression,
|
||||
final ConfigParseOptions baseOptions) {
|
||||
Path path = Parser.parsePath(expression);
|
||||
String basename = makeResourceBasename(path);
|
||||
return parseResourcesAnySyntax(ConfigImpl.class, basename, baseOptions);
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static ConfigObject parseResourcesAnySyntax(final Class<?> klass,
|
||||
String resourceBasename, final ConfigParseOptions baseOptions) {
|
||||
@ -281,16 +255,6 @@ public class ConfigImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
/* FIXME this is broken and will go away when Config.load() doesn't use it. */
|
||||
public static Config systemPropertiesWithPrefix(String rootPath) {
|
||||
try {
|
||||
return systemPropertiesAsConfigObject().toConfig().getConfig(rootPath);
|
||||
} catch (ConfigException.Missing e) {
|
||||
return emptyObject("system properties").toConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SimpleIncluder implements ConfigIncluder {
|
||||
|
||||
private ConfigIncluder fallback;
|
||||
@ -394,4 +358,17 @@ public class ConfigImpl {
|
||||
public static Config envVariablesAsConfig() {
|
||||
return envVariablesAsConfigObject().toConfig();
|
||||
}
|
||||
|
||||
private static class ReferenceHolder {
|
||||
private static final Config unresolvedResources = Parseable
|
||||
.newResources(ConfigImpl.class, "/reference.conf", ConfigParseOptions.defaults())
|
||||
.parse().toConfig();
|
||||
static final Config referenceConfig = systemPropertiesAsConfig().withFallback(
|
||||
unresolvedResources).resolve();
|
||||
}
|
||||
|
||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||
public static Config defaultReference() {
|
||||
return ReferenceHolder.referenceConfig;
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,35 @@
|
||||
</head>
|
||||
<body bgcolor="white">
|
||||
|
||||
<p>
|
||||
An API for loading and using configuration files, see <a href="https://github.com/havocp/config/">the project site</a>
|
||||
for more information.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use
|
||||
it with methods in the {@link com.typesafe.config.Config} interface.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An application can simply call {@link com.typesafe.config.ConfigFactory#load()} and place
|
||||
its configuration in "application.conf" on the classpath.
|
||||
If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()}
|
||||
there's no need to pass a configuration to your libraries
|
||||
and frameworks, as long as they all default to this same default, which they should.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A library or framework should ship a file "reference.conf" in its jar, and allow an application to pass in a
|
||||
{@link com.typesafe.config.Config} to be used for the library. If no {@link com.typesafe.config.Config} is provided,
|
||||
call {@link com.typesafe.config.ConfigFactory#load()}
|
||||
to get the default one. Typically a library might offer two constructors, one with a <code>Config</code> parameter
|
||||
and one which uses {@link com.typesafe.config.ConfigFactory#load()}.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can find an example app and library <a href="https://github.com/havocp/config/tree/master/examples">on GitHub</a>.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -2,7 +2,9 @@ import com.typesafe.config.ConfigFactory
|
||||
|
||||
object RenderExample extends App {
|
||||
def render(what: String) {
|
||||
val conf = ConfigFactory.loadWithoutResolving(what)
|
||||
val conf = ConfigFactory.defaultOverrides()
|
||||
.withFallback(ConfigFactory.parseResourcesAnySyntax(classOf[ConfigFactory], "/" + what))
|
||||
.withFallback(ConfigFactory.defaultReference())
|
||||
|
||||
println("=== BEGIN UNRESOLVED " + what)
|
||||
println(conf.root.render())
|
||||
|
@ -880,7 +880,7 @@ class ConfigTest extends TestUtils {
|
||||
@Test
|
||||
def renderRoundTrip() {
|
||||
for (i <- 1 to 6) {
|
||||
val conf = ConfigFactory.loadWithoutResolving("test0" + i)
|
||||
val conf = ConfigFactory.parseResourcesAnySyntax(classOf[ConfigTest], "test0" + i)
|
||||
val unresolvedRender = conf.root.render()
|
||||
val resolved = conf.resolve()
|
||||
val resolvedRender = resolved.root.render()
|
||||
|
@ -34,8 +34,8 @@ class PublicApiTest extends TestUtils {
|
||||
@Test
|
||||
def noSystemVariables() {
|
||||
// should not have used system variables
|
||||
val conf = ConfigFactory.load("test01", ConfigParseOptions.defaults(),
|
||||
ConfigResolveOptions.noSystem())
|
||||
val conf = ConfigFactory.parseResourcesAnySyntax(classOf[PublicApiTest], "/test01")
|
||||
.resolve(ConfigResolveOptions.noSystem())
|
||||
|
||||
intercept[ConfigException.Missing] {
|
||||
conf.getString("system.home")
|
||||
@ -389,7 +389,7 @@ class PublicApiTest extends TestUtils {
|
||||
|
||||
@Test
|
||||
def multipleResourcesUsed() {
|
||||
val conf = ConfigFactory.parseResources(classOf[PublicApiTest], "/test01.conf", ConfigParseOptions.defaults())
|
||||
val conf = ConfigFactory.parseResources(classOf[PublicApiTest], "/test01.conf")
|
||||
|
||||
assertEquals(42, conf.getInt("ints.fortyTwo"))
|
||||
assertEquals(true, conf.getBoolean("test-lib.fromTestLib"))
|
||||
|
2
examples/complex-app/src/main/resources/complex1.conf
Normal file
2
examples/complex-app/src/main/resources/complex1.conf
Normal file
@ -0,0 +1,2 @@
|
||||
simple-lib.foo="This value comes from complex-app's complex1.conf"
|
||||
simple-lib.whatever = "This value comes from complex-app's complex1.conf"
|
12
examples/complex-app/src/main/resources/complex2.conf
Normal file
12
examples/complex-app/src/main/resources/complex2.conf
Normal file
@ -0,0 +1,12 @@
|
||||
complex-app {
|
||||
# here we want a simple-lib-context unique to our app
|
||||
# which can be custom-configured. In code, we have to
|
||||
# pull out this subtree and pass it to simple-lib.
|
||||
|
||||
simple-lib-context = {
|
||||
simple-lib {
|
||||
foo="This value comes from complex-app's complex2.conf in its custom simple-lib-context"
|
||||
whatever = "This value comes from complex-app's complex2.conf in its custom simple-lib-context"
|
||||
}
|
||||
}
|
||||
}
|
51
examples/complex-app/src/main/scala/ComplexApp.scala
Normal file
51
examples/complex-app/src/main/scala/ComplexApp.scala
Normal file
@ -0,0 +1,51 @@
|
||||
import com.typesafe.config._
|
||||
import simplelib._
|
||||
|
||||
object ComplexApp extends App {
|
||||
// This app is "complex" because we load multiple separate app
|
||||
// configs into a single JVM and we have a separately-configurable
|
||||
// context for simple lib.
|
||||
|
||||
System.setProperty("simple-lib.whatever", "This value comes from a system property")
|
||||
|
||||
// "config1" is just an example of using a file other than application.conf
|
||||
val config1 = ConfigFactory.load("complex1")
|
||||
|
||||
demoConfigInSimpleLib(config1)
|
||||
|
||||
// "config2" shows how to configure a library with a custom settings subtree
|
||||
val config2 = ConfigFactory.load("complex2");
|
||||
|
||||
// pull out complex-app.simple-lib-context and move it to
|
||||
// the toplevel, creating a new config suitable for our SimpleLibContext.
|
||||
// The defaultOverrides() have to be put back on top of the stack so
|
||||
// they still override any simple-lib settings.
|
||||
// We fall back to config2 again to be sure we get simple-lib's
|
||||
// reference.conf plus any other settings we've set. You could
|
||||
// also just fall back to ConfigFactory.referenceConfig() if
|
||||
// you don't want application.conf settings outside of
|
||||
// complex-app.simple-lib-context to be used.
|
||||
val simpleLibConfig2 = ConfigFactory.defaultOverrides()
|
||||
.withFallback(config2.getConfig("complex-app.simple-lib-context"))
|
||||
.withFallback(config2)
|
||||
|
||||
demoConfigInSimpleLib(simpleLibConfig2)
|
||||
|
||||
// Now let's illustrate that simple-lib will get upset if we pass it
|
||||
// a bad config. In this case, we'll fail to merge the reference
|
||||
// config in to complex-app.simple-lib-context, so simple-lib will
|
||||
// point out that some settings are missing.
|
||||
try {
|
||||
demoConfigInSimpleLib(config2.getConfig("complex-app.simple-lib-context"))
|
||||
} catch {
|
||||
case e: ConfigException.ValidationFailed =>
|
||||
println("when we passed a bad config to simple-lib, it said: " + e.getMessage)
|
||||
}
|
||||
|
||||
def demoConfigInSimpleLib(config: Config) {
|
||||
val context = new SimpleLibContext(config)
|
||||
context.printSetting("simple-lib.foo")
|
||||
context.printSetting("simple-lib.hello")
|
||||
context.printSetting("simple-lib.whatever")
|
||||
}
|
||||
}
|
2
examples/simple-app/src/main/resources/application.conf
Normal file
2
examples/simple-app/src/main/resources/application.conf
Normal file
@ -0,0 +1,2 @@
|
||||
simple-lib.foo="This value comes from simple-app's application.conf"
|
||||
simple-lib.whatever = "This value comes from simple-app's application.conf"
|
14
examples/simple-app/src/main/scala/SimpleApp.scala
Normal file
14
examples/simple-app/src/main/scala/SimpleApp.scala
Normal file
@ -0,0 +1,14 @@
|
||||
import com.typesafe.config._
|
||||
import simplelib._
|
||||
|
||||
object SimpleApp extends App {
|
||||
// example of how system properties override
|
||||
System.setProperty("simple-lib.whatever", "This value comes from a system property")
|
||||
|
||||
// In this simple app, we're allowing SimpleLibContext() to
|
||||
// use the default config in application.conf
|
||||
val context = new SimpleLibContext()
|
||||
context.printSetting("simple-lib.foo")
|
||||
context.printSetting("simple-lib.hello")
|
||||
context.printSetting("simple-lib.whatever")
|
||||
}
|
5
examples/simple-lib/src/main/resources/reference.conf
Normal file
5
examples/simple-lib/src/main/resources/reference.conf
Normal file
@ -0,0 +1,5 @@
|
||||
simple-lib {
|
||||
foo = "This value comes from simple-lib's reference.conf"
|
||||
hello = "This value comes from simple-lib's reference.conf"
|
||||
whatever = "This value comes from simple-lib's reference.conf"
|
||||
}
|
22
examples/simple-lib/src/main/scala/simplelib/SimpleLib.scala
Normal file
22
examples/simple-lib/src/main/scala/simplelib/SimpleLib.scala
Normal file
@ -0,0 +1,22 @@
|
||||
package simplelib
|
||||
|
||||
import com.typesafe.config._
|
||||
|
||||
// we have a constructor allowing the app to provide a custom Config
|
||||
class SimpleLibContext(config: Config) {
|
||||
|
||||
// This verifies that the Config is sane and has our
|
||||
// reference config.
|
||||
config.checkValid(ConfigFactory.defaultReference())
|
||||
|
||||
// This uses the standard default Config, if none is provided,
|
||||
// which simplifies apps willing to use the defaults
|
||||
def this() {
|
||||
this(ConfigFactory.load())
|
||||
}
|
||||
|
||||
// this is the amazing functionality provided by simple-lib
|
||||
def printSetting(path: String) {
|
||||
println("The setting '" + path + "' is: " + config.getString(path))
|
||||
}
|
||||
}
|
@ -3,11 +3,20 @@ import Keys._
|
||||
|
||||
object ConfigBuild extends Build {
|
||||
lazy val root = Project(id = "root",
|
||||
base = file(".")) aggregate(testLib, configLib)
|
||||
base = file(".")) aggregate(testLib, configLib, simpleLib, simpleApp)
|
||||
|
||||
lazy val configLib = Project(id = "config",
|
||||
base = file("config")) dependsOn(testLib % "test->test")
|
||||
|
||||
lazy val testLib = Project(id = "test-lib",
|
||||
base = file("test-lib"))
|
||||
|
||||
lazy val simpleLib = Project(id = "simple-lib",
|
||||
base = file("examples/simple-lib")) dependsOn(configLib)
|
||||
|
||||
lazy val simpleApp = Project(id = "simple-app",
|
||||
base = file("examples/simple-app")) dependsOn(simpleLib)
|
||||
|
||||
lazy val complexApp = Project(id = "complex-app",
|
||||
base = file("examples/complex-app")) dependsOn(simpleLib)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user