com.crashnote.external.config.ConfigFactory Maven / Gradle / Ivy
Show all versions of crashnote-appengine Show documentation
/**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.crashnote.external.config;
import java.io.File;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import com.crashnote.external.config.impl.ConfigImpl;
import com.crashnote.external.config.impl.Parseable;
/**
* Contains static methods for creating {@link Config} instances.
*
*
* See also {@link ConfigValueFactory} which contains static methods for
* converting Java values into a {@link ConfigObject}. You can then convert a
* {@code ConfigObject} into a {@code Config} with {@link 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 {@link ConfigValue}
* from a resource and nothing else.
*/
public final class ConfigFactory {
private ConfigFactory() {
}
/**
* 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}).
*
*
* Resources are loaded from the current thread's
* {@link 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
* {@link #load(Config)}.
*
* @param resourceBasename
* name (optionally without extension) of a resource on classpath
* @return configuration for an application relative to context class loader
*/
public static Config load(final String resourceBasename) {
return load(resourceBasename, ConfigParseOptions.defaults(),
ConfigResolveOptions.defaults());
}
/**
* Like {@link #load(String)} but uses the supplied class loader instead of
* the current thread's context class loader.
*
* @param loader
* @param resourceBasename
* @return configuration for an application relative to given class loader
*/
public static Config load(final ClassLoader loader, final String resourceBasename) {
return load(resourceBasename, ConfigParseOptions.defaults().setClassLoader(loader),
ConfigResolveOptions.defaults());
}
/**
* Like {@link #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
*/
public static Config load(final String resourceBasename, final ConfigParseOptions parseOptions,
final ConfigResolveOptions resolveOptions) {
final Config appConfig = ConfigFactory.parseResourcesAnySyntax(resourceBasename, parseOptions);
return load(parseOptions.getClassLoader(), appConfig, resolveOptions);
}
/**
* Like {@link #load(String,ConfigParseOptions,ConfigResolveOptions)} but
* has a class loader parameter that overrides any from the
* {@code 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
*/
public static Config load(final ClassLoader loader, final String resourceBasename,
final ConfigParseOptions parseOptions, final ConfigResolveOptions resolveOptions) {
return load(resourceBasename, parseOptions.setClassLoader(loader), resolveOptions);
}
/**
* 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
*/
public static Config load(final Config config) {
return load(Thread.currentThread().getContextClassLoader(), config);
}
public static Config load(final ClassLoader loader, final Config config) {
return load(loader, config, ConfigResolveOptions.defaults());
}
/**
* Like {@link #load(Config)} but allows you to specify
* {@link 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
*/
public static Config load(final Config config, final ConfigResolveOptions resolveOptions) {
return load(Thread.currentThread().getContextClassLoader(), config, resolveOptions);
}
/**
* Like {@link #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
*/
public static Config load(final ClassLoader loader, final Config config, final ConfigResolveOptions resolveOptions) {
return defaultOverrides(loader).withFallback(config).withFallback(defaultReference(loader))
.resolve(resolveOptions);
}
private static Config loadDefaultConfig(final ClassLoader loader) {
return loadDefaultConfig(loader, ConfigParseOptions.defaults());
}
private static Config loadDefaultConfig(final ClassLoader loader, final ConfigParseOptions parseOptions) {
return loadDefaultConfig(loader, parseOptions, ConfigResolveOptions.defaults());
}
private static Config loadDefaultConfig(final ClassLoader loader, final ConfigResolveOptions resolveOptions) {
return loadDefaultConfig(loader, ConfigParseOptions.defaults(), resolveOptions);
}
private static Config loadDefaultConfig(final ClassLoader loader, final ConfigParseOptions parseOptions, final ConfigResolveOptions resolveOptions) {
int specified = 0;
// override application.conf with config.file, config.resource,
// config.url if requested.
String resource = System.getProperty("config.resource");
if (resource != null)
specified += 1;
final String file = System.getProperty("config.file");
if (file != null)
specified += 1;
final String url = System.getProperty("config.url");
if (url != null)
specified += 1;
if (specified == 0) {
return load(loader, "application", parseOptions, resolveOptions);
} else if (specified > 1) {
throw new ConfigException.Generic("You set more than one of config.file='" + file
+ "', config.url='" + url + "', config.resource='" + resource
+ "'; don't know which one to use!");
} else {
if (resource != null) {
if (resource.startsWith("/"))
resource = resource.substring(1);
// this deliberately does not parseResourcesAnySyntax; if
// people want that they can use an include statement.
return load(loader, parseResources(loader, resource, parseOptions), resolveOptions);
} else if (file != null) {
return load(
loader,
parseFile(new File(file), parseOptions), resolveOptions);
} else {
try {
return load(
loader,
parseURL(new URL(url), parseOptions), resolveOptions);
} catch (MalformedURLException e) {
throw new ConfigException.Generic("Bad URL in config.url system property: '"
+ url + "': " + e.getMessage(), e);
}
}
}
}
/**
* Loads a default configuration, equivalent to {@link #load(String)
* load("application")} 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 {@link #invalidateCaches()} to
* force it to reload.)
*
* 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 {@link #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, ConfigFactory.load()
is equivalent to
* ConfigFactory.load("application")
.
*
* @return configuration for an application
*/
public static Config load() {
return load(Thread.currentThread().getContextClassLoader());
}
/**
* Like {@link #load()} but allows specifying parse options
*
* @param parseOptions
* Options for parsing resources
* @return configuration for an application
*/
public static Config load(final ConfigParseOptions parseOptions) {
return load(Thread.currentThread().getContextClassLoader(), parseOptions);
}
/**
* Like {@link #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
*/
public static Config load(final ClassLoader loader) {
return ConfigImpl.computeCachedConfig(loader, "load", new Callable() {
@Override
public Config call() {
return loadDefaultConfig(loader);
}
});
}
/**
* Like {@link #load()} but allows specifying a class loader other than the
* thread's current context class loader, and parse options
*
* @param loader
* class loader for finding resources
* @param parseOptions
* Options for parsing resources
* @return configuration for an application
*/
public static Config load(final ClassLoader loader, final ConfigParseOptions parseOptions) {
return loadDefaultConfig(loader, parseOptions);
}
/**
* Like {@link #load()} but allows specifying a class loader other than the
* thread's current context class loader, and resolve options
*
* @param loader
* class loader for finding resources
* @param resolveOptions
* options for resolving the assembled config stack
* @return configuration for an application
*/
public static Config load(final ClassLoader loader, final ConfigResolveOptions resolveOptions) {
return loadDefaultConfig(loader, resolveOptions);
}
/**
* Like {@link #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
* @param parseOptions
* Options for parsing resources
* @param resolveOptions
* options for resolving the assembled config stack
* @return configuration for an application
*/
public static Config load(final ClassLoader loader, final ConfigParseOptions parseOptions, final ConfigResolveOptions resolveOptions) {
return loadDefaultConfig(loader, parseOptions, 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
* {@link #defaultReference(ClassLoader)} if the context class loader is not
* suitable.
*
*
* The {@link #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
*/
public static Config defaultReference() {
return defaultReference(Thread.currentThread().getContextClassLoader());
}
/**
* Like {@link #defaultReference()} but allows you to specify a class loader
* to use rather than the current context class loader.
*
* @param loader
* @return the default reference config for this class loader
*/
public static Config defaultReference(final ClassLoader loader) {
return ConfigImpl.defaultReference(loader);
}
/**
* Obtains the default override configuration, which currently consists of
* system properties. The returned override configuration will already have
* substitutions resolved.
*
*
* The {@link #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
*/
public static Config defaultOverrides() {
return systemProperties();
}
/**
* Like {@link #defaultOverrides()} but allows you to specify a class loader
* to use rather than the current context class loader.
*
* @param loader
* @return the default override configuration
*/
public static Config defaultOverrides(final ClassLoader loader) {
return systemProperties();
}
/**
* Reloads any cached configs, picking up changes to system properties for
* example. Because a {@link Config} is immutable, anyone with a reference
* to the old configs will still have the same outdated objects. However,
* new calls to {@link #load()} or {@link #defaultOverrides()} or
* {@link #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 {@link #load()}, will work. Make changes
* before you invalidate.
*/
public static void invalidateCaches() {
// We rely on this having the side effect that it drops
// all caches
ConfigImpl.reloadSystemPropertiesConfig();
}
/**
* 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 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 {@link Config#withFallback}, making
* the description more useful.)
*
* @param originDescription
* description of the config
* @return an empty configuration
*/
public static Config empty(final String originDescription) {
return ConfigImpl.emptyConfig(originDescription);
}
/**
* Gets a Config
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.
*
* {@link #load} will include the system properties as overrides already, as
* will {@link #defaultReference} and {@link #defaultOverrides}.
*
*
* Because this returns a singleton, it will not notice changes to system
* properties made after the first time this method is called. Use
* {@link #invalidateCaches()} to force the singleton to reload if you
* modify system properties.
*
* @return system properties parsed into a Config
*/
public static Config systemProperties() {
return 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
* {@link java.lang.System#getenv(String)} though, since you can use methods
* such as {@link Config#getInt}.
*
* @return system environment variables parsed into a Config
*/
public static Config systemEnvironment() {
return ConfigImpl.envVariablesAsConfig();
}
/**
* Converts a Java {@link java.util.Properties} object to a
* {@link ConfigObject} using the rules documented in the 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 {@link #systemProperties()} method
* which returns a cached global singleton.
*
* @param properties
* a Java Properties object
* @param options
* @return the parsed configuration
*/
public static Config parseProperties(final Properties properties,
final ConfigParseOptions options) {
return Parseable.newProperties(properties, options).parse().toConfig();
}
public static Config parseProperties(final Properties properties) {
return parseProperties(properties, ConfigParseOptions.defaults());
}
public static Config parseReader(final Reader reader, final ConfigParseOptions options) {
return Parseable.newReader(reader, options).parse().toConfig();
}
public static Config parseReader(final Reader reader) {
return parseReader(reader, ConfigParseOptions.defaults());
}
public static Config parseURL(final URL url, final ConfigParseOptions options) {
return Parseable.newURL(url, options).parse().toConfig();
}
public static Config parseURL(final URL url) {
return parseURL(url, ConfigParseOptions.defaults());
}
public static Config parseFile(final File file, final ConfigParseOptions options) {
return Parseable.newFile(file, options).parse().toConfig();
}
public static Config parseFile(final File file) {
return 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
* {@link ConfigSyntax#CONF}, ".json" forces {@link ConfigSyntax#JSON}, and
* ".properties" forces {@link 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 {@link ConfigParseOptions#getAllowMissing options.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
*/
public static Config parseFileAnySyntax(final File fileBasename,
final ConfigParseOptions options) {
return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig();
}
public static Config parseFileAnySyntax(final File fileBasename) {
return 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
* {@link java.lang.Class#getResource}.
*
*
* Duplicate resources with the same name are merged such that ones returned
* earlier from {@link 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
*/
public static Config parseResources(final Class> klass, final String resource,
final ConfigParseOptions options) {
return Parseable.newResources(klass, resource, options).parse()
.toConfig();
}
public static Config parseResources(final Class> klass, final String resource) {
return parseResources(klass, resource, ConfigParseOptions.defaults());
}
/**
* Parses classpath resources with a flexible extension. In general, this
* method has the same behavior as
* {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath
* resources instead, as in {@link #parseResources}.
*
*
* There is a thorny problem with this method, which is that
* {@link 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
* {@link #parseFileAnySyntax(File,ConfigParseOptions) parseFileAnySyntax()}
* - 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 {@link 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
*/
public static Config parseResourcesAnySyntax(final Class> klass, final String resourceBasename,
final ConfigParseOptions options) {
return ConfigImpl.parseResourcesAnySyntax(klass, resourceBasename,
options).toConfig();
}
public static Config parseResourcesAnySyntax(final Class> klass, final String resourceBasename) {
return 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 {@link java.lang.ClassLoader#getResource}, not like
* {@link java.lang.Class#getResource}, so the name never begins with a
* slash.
*
*
* See {@link #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
*/
public static Config parseResources(final ClassLoader loader, final String resource,
final ConfigParseOptions options) {
return Parseable.newResources(resource, options.setClassLoader(loader)).parse().toConfig();
}
public static Config parseResources(final ClassLoader loader, final String resource) {
return parseResources(loader, resource, ConfigParseOptions.defaults());
}
/**
* Parses classpath resources with a flexible extension. In general, this
* method has the same behavior as
* {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath
* resources instead, as in
* {@link #parseResources(ClassLoader,String,ConfigParseOptions)}.
*
*
* {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} differs
* in the syntax for the resource name, but otherwise see
* {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} 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
* {@link java.lang.ClassLoader#getResource}, with or without
* extension
* @param options
* parse options (class loader ignored)
* @return the parsed configuration
*/
public static Config parseResourcesAnySyntax(final ClassLoader loader, final String resourceBasename,
final ConfigParseOptions options) {
return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options.setClassLoader(loader))
.toConfig();
}
public static Config parseResourcesAnySyntax(final ClassLoader loader, final String resourceBasename) {
return parseResourcesAnySyntax(loader, resourceBasename, ConfigParseOptions.defaults());
}
/**
* Like {@link #parseResources(ClassLoader,String,ConfigParseOptions)} but
* uses thread's current context class loader.
*/
public static Config parseResources(final String resource, final ConfigParseOptions options) {
return Parseable.newResources(resource, options)
.parse().toConfig();
}
/**
* Like {@link #parseResources(ClassLoader,String)} but uses thread's
* current context class loader.
*/
public static Config parseResources(final String resource) {
return parseResources(resource, ConfigParseOptions.defaults());
}
/**
* Like
* {@link #parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)}
* but uses thread's current context class loader.
*/
public static Config parseResourcesAnySyntax(final String resourceBasename, final ConfigParseOptions options) {
return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options).toConfig();
}
/**
* Like {@link #parseResourcesAnySyntax(ClassLoader,String)} but uses
* thread's current context class loader.
*/
public static Config parseResourcesAnySyntax(final String resourceBasename) {
return parseResourcesAnySyntax(resourceBasename, ConfigParseOptions.defaults());
}
public static Config parseString(final String s, final ConfigParseOptions options) {
return Parseable.newString(s, options).parse().toConfig();
}
public static Config parseString(final String s) {
return parseString(s, ConfigParseOptions.defaults());
}
/**
* Creates a {@code Config} based on a {@link java.util.Map} from paths to
* plain Java values. Similar to
* {@link ConfigValueFactory#fromMap(Map,String)}, except the keys in the
* map are path expressions, rather than keys; and correspondingly it
* returns a {@code Config} instead of a {@code 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
* @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 {@code Config}
*/
public static Config parseMap(final Map values,
final String originDescription) {
return ConfigImpl.fromPathMap(values, originDescription).toConfig();
}
/**
* See the other overload of {@link #parseMap(Map, String)} for details,
* this one just uses a default origin description.
*
* @param values
* @return the map converted to a {@code Config}
*/
public static Config parseMap(final Map values) {
return parseMap(values, null);
}
}