pax_global_header 0000666 0000000 0000000 00000000064 12771472746 0014533 g ustar 00root root 0000000 0000000 52 comment=82e1b25f62cf6add4f64b5d909c56a653f449f4a
config-1.3.1/ 0000775 0000000 0000000 00000000000 12771472746 0013002 5 ustar 00root root 0000000 0000000 config-1.3.1/.gitignore 0000664 0000000 0000000 00000000142 12771472746 0014767 0 ustar 00root root 0000000 0000000 .classpath
.project
.cache
.settings
.idea
.idea_modules
/bin-test-lib
target/
/bin
/node_modules
config-1.3.1/.travis.yml 0000664 0000000 0000000 00000001270 12771472746 0015113 0 ustar 00root root 0000000 0000000 # use Docker-based container (instead of OpenVZ)
sudo: false
cache:
directories:
- $HOME/.ivy2/cache
# Cache the sbt launcher, currently the Travis VM preinstalls 0.13.5
- $HOME/.sbt/launchers/0.13.7
# Cache scala, currently the Travis VM preinstalls 2.11.2 & 2.10.4
#- $HOME/.sbt/boot/scala-$TRAVIS_SCALA_VERSION
# Updates regarding Travis VM preinstalls:
# https://github.com/travis-ci/travis-cookbooks/blob/master/changes.md
language: scala
jdk:
- oraclejdk8
script:
- sbt ++$TRAVIS_SCALA_VERSION test doc
# Remove to avoid unnecessary cache updates
- find $HOME/.sbt -name "*.lock" -delete
- find $HOME/.ivy2 -name "ivydata-*.properties" -delete
config-1.3.1/CONTRIBUTING.md 0000664 0000000 0000000 00000004662 12771472746 0015243 0 ustar 00root root 0000000 0000000 # Submitting pull requests
Pull requests should go via GitHub; there are some nice
[general guidelines for contributing on GitHub](https://guides.github.com/activities/contributing-to-open-source/)
if you haven't done it before.
Unless your fix is trivial, it's a good idea to look for related
discussions and maybe open an issue to discuss, before making a
pull request. Discussion in advance may keep you from wasting time
on an approach that won't work out. However, if you communicate
better in code than in words, starting with a patch is
fine... just be willing to revise it!
Before we can accept pull requests, you will need to agree to the
Typesafe Contributor License Agreement online, using your GitHub
account - it takes 30 seconds. You can do this at
http://www.typesafe.com/contribute/cla
Expect that most PRs will need revision before merge. If people
suggest revisions, you can make them yourself or wait for a
maintainer to do it on their own timeline. The larger the PR, the
more revision will likely be needed.
# Making a release
To make a release you'll need to be a maintainer with GitHub
permissions to push to the master and gh-pages branches, and
Sonatype permissions to publish.
Here are the steps, which should be automated but aren't (PR
welcome!):
1. write release notes in NEWS.md following the format
already in there. commit.
2. create a signed git tag "vX.Y.Z"
3. start sbt; `show version` should confirm that the version was
taken from the tag
4. clean
5. test (double check that release works)
6. doc (double check that docs build, plus build docs
to be copied to gh-pages later)
7. if test or doc fails, delete the tag, fix it, start over.
8. publishSigned
9. make a separate clone of the repo in another directory and
check out the gh-pages branch
10. /bin/rm -rf latest/api on gh-pages checkout
11. copy config/target/api from master checkout to vX.Y.Z in
gh-pages so you have vX.Y.Z/index.html
12. copy config/target/api from master checkout into latest/
so you have latest/api/index.html
13. commit all that to gh-pages, check the diff for sanity
(latest/api should be mostly just changed timestamps)
14. push gh-pages
15. log into sonatype website and go through the usual hoops
(search for com.typesafe, verify the artifacts in it, close,
release)
16. push the "vX.Y.Z" tag
17. announce release, possibly wait for maven central to sync
first
config-1.3.1/HOCON.md 0000664 0000000 0000000 00000170501 12771472746 0014176 0 ustar 00root root 0000000 0000000
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [HOCON (Human-Optimized Config Object Notation)](#hocon-human-optimized-config-object-notation)
- [Goals / Background](#goals--background)
- [Definitions](#definitions)
- [Syntax](#syntax)
- [Unchanged from JSON](#unchanged-from-json)
- [Comments](#comments)
- [Omit root braces](#omit-root-braces)
- [Key-value separator](#key-value-separator)
- [Commas](#commas)
- [Whitespace](#whitespace)
- [Duplicate keys and object merging](#duplicate-keys-and-object-merging)
- [Unquoted strings](#unquoted-strings)
- [Multi-line strings](#multi-line-strings)
- [Value concatenation](#value-concatenation)
- [String value concatenation](#string-value-concatenation)
- [Array and object concatenation](#array-and-object-concatenation)
- [Note: Concatenation with whitespace and substitutions](#note-concatenation-with-whitespace-and-substitutions)
- [Note: Arrays without commas or newlines](#note-arrays-without-commas-or-newlines)
- [Path expressions](#path-expressions)
- [Paths as keys](#paths-as-keys)
- [Substitutions](#substitutions)
- [Self-Referential Substitutions](#self-referential-substitutions)
- [The `+=` field separator](#the--field-separator)
- [Examples of Self-Referential Substitutions](#examples-of-self-referential-substitutions)
- [Includes](#includes)
- [Include syntax](#include-syntax)
- [Include semantics: merging](#include-semantics-merging)
- [Include semantics: substitution](#include-semantics-substitution)
- [Include semantics: missing files](#include-semantics-missing-files)
- [Include semantics: file formats and extensions](#include-semantics-file-formats-and-extensions)
- [Include semantics: locating resources](#include-semantics-locating-resources)
- [Conversion of numerically-indexed objects to arrays](#conversion-of-numerically-indexed-objects-to-arrays)
- [MIME Type](#mime-type)
- [API Recommendations](#api-recommendations)
- [Automatic type conversions](#automatic-type-conversions)
- [Units format](#units-format)
- [Duration format](#duration-format)
- [Size in bytes format](#size-in-bytes-format)
- [Config object merging and file merging](#config-object-merging-and-file-merging)
- [Java properties mapping](#java-properties-mapping)
- [Conventional configuration files for JVM apps](#conventional-configuration-files-for-jvm-apps)
- [Conventional override by system properties](#conventional-override-by-system-properties)
- [Substitution fallback to environment variables](#substitution-fallback-to-environment-variables)
- [hyphen-separated vs. camelCase](#hyphen-separated-vs-camelcase)
- [Note on Java properties similarity](#note-on-java-properties-similarity)
# HOCON (Human-Optimized Config Object Notation)
This is an informal spec, but hopefully it's clear.
## Goals / Background
The primary goal is: keep the semantics (tree structure; set of
types; encoding/escaping) from JSON, but make it more convenient
as a human-editable config file format.
The following features are desirable, to support human usage:
- less noisy / less pedantic syntax
- ability to refer to another part of the configuration (set a value to
another value)
- import/include another configuration file into the current file
- a mapping to a flat properties list such as Java's system properties
- ability to get values from environment variables
- ability to write comments
Implementation-wise, the format should have these properties:
- a JSON superset, that is, all valid JSON should be valid and
should result in the same in-memory data that a JSON parser
would have produced.
- be deterministic; the format is flexible, but it is not
heuristic. It should be clear what's invalid and invalid files
should generate errors.
- require minimal look-ahead; should be able to tokenize the file
by looking at only the next three characters. (right now, the
only reason to look at three is to find "//" comments;
otherwise you can parse looking at two.)
HOCON is significantly harder to specify and to parse than
JSON. Think of it as moving the work from the person maintaining
the config file to the computer program.
## Definitions
- a _key_ is a string JSON would have to the left of `:` and a _value_ is
anything JSON would have to the right of `:`. i.e. the two
halves of an object _field_.
- a _value_ is any "value" as defined in the JSON spec, plus
unquoted strings and substitutions as defined in this spec.
- a _simple value_ is any value excluding an object or array
value.
- a _field_ is a key, any separator such as ':', and a value.
- references to a _file_ ("the file being parsed") can be
understood to mean any byte stream being parsed, not just
literal files in a filesystem.
## Syntax
Much of this is defined with reference to JSON; you can find the
JSON spec at http://json.org/ of course.
### Unchanged from JSON
- files must be valid UTF-8
- quoted strings are in the same format as JSON strings
- values have possible types: string, number, object, array, boolean, null
- allowed number formats matches JSON; as in JSON, some possible
floating-point values are not represented, such as `NaN`
### Comments
Anything between `//` or `#` and the next newline is considered a comment
and ignored, unless the `//` or `#` is inside a quoted string.
### Omit root braces
JSON documents must have an array or object at the root. Empty
files are invalid documents, as are files containing only a
non-array non-object value such as a string.
In HOCON, if the file does not begin with a square bracket or
curly brace, it is parsed as if it were enclosed with `{}` curly
braces.
A HOCON file is invalid if it omits the opening `{` but still has
a closing `}`; the curly braces must be balanced.
### Key-value separator
The `=` character can be used anywhere JSON allows `:`, i.e. to
separate keys from values.
If a key is followed by `{`, the `:` or `=` may be omitted. So
`"foo" {}` means `"foo" : {}`
### Commas
Values in arrays, and fields in objects, need not have a comma
between them as long as they have at least one ASCII newline
(`\n`, decimal value 10) between them.
The last element in an array or last field in an object may be
followed by a single comma. This extra comma is ignored.
- `[1,2,3,]` and `[1,2,3]` are the same array.
- `[1\n2\n3]` and `[1,2,3]` are the same array.
- `[1,2,3,,]` is invalid because it has two trailing commas.
- `[,1,2,3]` is invalid because it has an initial comma.
- `[1,,2,3]` is invalid because it has two commas in a row.
- these same comma rules apply to fields in objects.
### Whitespace
The JSON spec simply says "whitespace"; in HOCON whitespace is
defined as follows:
- any Unicode space separator (Zs category), line separator (Zl
category), or paragraph separator (Zp category), including
nonbreaking spaces (such as 0x00A0, 0x2007, and 0x202F).
The BOM (0xFEFF) must also be treated as whitespace.
- tab (`\t` 0x0009), newline ('\n' 0x000A), vertical tab ('\v'
0x000B)`, form feed (`\f' 0x000C), carriage return ('\r'
0x000D), file separator (0x001C), group separator (0x001D),
record separator (0x001E), unit separator (0x001F).
In Java, the `isWhitespace()` method covers these characters with
the exception of nonbreaking spaces and the BOM.
While all Unicode separators should be treated as whitespace, in
this spec "newline" refers only and specifically to ASCII newline
0x000A.
### Duplicate keys and object merging
The JSON spec does not clarify how duplicate keys in the same
object should be handled. In HOCON, duplicate keys that appear
later override those that appear earlier, unless both values are
objects. If both values are objects, then the objects are merged.
Note: this would make HOCON a non-superset of JSON if you assume
that JSON requires duplicate keys to have a behavior. The
assumption here is that duplicate keys are invalid JSON.
To merge objects:
- add fields present in only one of the two objects to the merged
object.
- for non-object-valued fields present in both objects,
the field found in the second object must be used.
- for object-valued fields present in both objects, the
object values should be recursively merged according to
these same rules.
Object merge can be prevented by setting the key to another value
first. This is because merging is always done two values at a
time; if you set a key to an object, a non-object, then an object,
first the non-object falls back to the object (non-object always
wins), and then the object falls back to the non-object (no
merging, object is the new value). So the two objects never see
each other.
These two are equivalent:
{
"foo" : { "a" : 42 },
"foo" : { "b" : 43 }
}
{
"foo" : { "a" : 42, "b" : 43 }
}
And these two are equivalent:
{
"foo" : { "a" : 42 },
"foo" : null,
"foo" : { "b" : 43 }
}
{
"foo" : { "b" : 43 }
}
The intermediate setting of `"foo"` to `null` prevents the object merge.
### Unquoted strings
A sequence of characters outside of a quoted string is a string
value if:
- it does not contain "forbidden characters": '$', '"', '{', '}',
'[', ']', ':', '=', ',', '+', '#', '`', '^', '?', '!', '@',
'*', '&', '\' (backslash), or whitespace.
- it does not contain the two-character string "//" (which
starts a comment)
- its initial characters do not parse as `true`, `false`, `null`,
or a number.
Unquoted strings are used literally, they do not support any kind
of escaping. Quoted strings may always be used as an alternative
when you need to write a character that is not permitted in an
unquoted string.
`truefoo` parses as the boolean token `true` followed by the
unquoted string `foo`. However, `footrue` parses as the unquoted
string `footrue`. Similarly, `10.0bar` is the number `10.0` then
the unquoted string `bar` but `bar10.0` is the unquoted string
`bar10.0`. (In practice, this distinction doesn't matter much
because of value concatenation; see later section.)
In general, once an unquoted string begins, it continues until a
forbidden character or the two-character string "//" is
encountered. Embedded (non-initial) booleans, nulls, and numbers
are not recognized as such, they are part of the string.
An unquoted string may not _begin_ with the digits 0-9 or with a
hyphen (`-`, 0x002D) because those are valid characters to begin a
JSON number. The initial number character, plus any valid-in-JSON
number characters that follow it, must be parsed as a number
value. Again, these characters are not special _inside_ an
unquoted string; they only trigger number parsing if they appear
initially.
Note that quoted JSON strings may not contain control characters
(control characters include some whitespace characters, such as
newline). This rule is from the JSON spec. However, unquoted
strings have no restriction on control characters, other than the
ones listed as "forbidden characters" above.
Some of the "forbidden characters" are forbidden because they
already have meaning in JSON or HOCON, others are essentially
reserved keywords to allow future extensions to this spec.
### Multi-line strings
Multi-line strings are similar to Python or Scala, using triple
quotes. If the three-character sequence `"""` appears, then all
Unicode characters until a closing `"""` sequence are used
unmodified to create a string value. Newlines and whitespace
receive no special treatment. Unlike Scala, and unlike JSON quoted
strings, Unicode escapes are not interpreted in triple-quoted
strings.
In Python, `"""foo""""` is a syntax error (a triple-quoted string
followed by a dangling unbalanced quote). In Scala, it is a
four-character string `foo"`. HOCON works like Scala; any sequence
of at least three quotes ends the multi-line string, and any
"extra" quotes are part of the string.
### Value concatenation
The value of an object field or array element may consist of
multiple values which are combined. There are three kinds of value
concatenation:
- if all the values are simple values (neither objects nor
arrays), they are concatenated into a string.
- if all the values are arrays, they are concatenated into
one array.
- if all the values are objects, they are merged (as with
duplicate keys) into one object.
String value concatenation is allowed in field keys, in addition
to field values and array elements. Objects and arrays do not make
sense as field keys.
Note: Akka 2.0 (and thus Play 2.0) contains an embedded
implementation of the config lib which does not support array and
object value concatenation; it only supports string value
concatenation.
#### String value concatenation
String value concatenation is the trick that makes unquoted
strings work; it also supports substitutions (`${foo}` syntax) in
strings.
Only simple values participate in string value
concatenation. Recall that a simple value is any value other than
arrays and objects.
As long as simple values are separated only by non-newline
whitespace, the _whitespace between them is preserved_ and the
values, along with the whitespace, are concatenated into a string.
String value concatenations never span a newline, or a character
that is not part of a simple value.
A string value concatenation may appear in any place that a string
may appear, including object keys, object values, and array
elements.
Whenever a value would appear in JSON, a HOCON parser instead
collects multiple values (including the whitespace between them)
and concatenates those values into a string.
Whitespace before the first and after the last simple value must
be discarded. Only whitespace _between_ simple values must be
preserved.
So for example ` foo bar baz ` parses as three unquoted strings,
and the three are value-concatenated into one string. The inner
whitespace is kept and the leading and trailing whitespace is
trimmed. The equivalent string, written in quoted form, would be
`"foo bar baz"`.
Value concatenating `foo bar` (two unquoted strings with
whitespace) and quoted string `"foo bar"` would result in the same
in-memory representation, seven characters.
For purposes of string value concatenation, non-string values are
converted to strings as follows (strings shown as quoted strings):
- `true` and `false` become the strings `"true"` and `"false"`.
- `null` becomes the string `"null"`.
- quoted and unquoted strings are themselves.
- numbers should be kept as they were originally written in the
file. For example, if you parse `1e5` then you might render
it alternatively as `1E5` with capital `E`, or just `100000`.
For purposes of value concatenation, it should be rendered
as it was written in the file.
- a substitution is replaced with its value which is then
converted to a string as above.
- it is invalid for arrays or objects to appear in a string value
concatenation.
A single value is never converted to a string. That is, it would
be wrong to value concatenate `true` by itself; that should be
parsed as a boolean-typed value. Only `true foo` (`true` with
another simple value on the same line) should be parsed as a value
concatenation and converted to a string.
#### Array and object concatenation
Arrays can be concatenated with arrays, and objects with objects,
but it is an error if they are mixed.
For purposes of concatenation, "array" also means "substitution
that resolves to an array" and "object" also means "substitution
that resolves to an object."
Within an field value or array element, if only non-newline
whitespace separates the end of a first array or object or
substitution from the start of a second array or object or
substitution, the two values are concatenated. Newlines may occur
_within_ the array or object, but not _between_ them. Newlines
_between_ prevent concatenation.
For objects, "concatenation" means "merging", so the second object
overrides the first.
Arrays and objects cannot be field keys, whether concatenation is
involved or not.
Here are several ways to define `a` to the same object value:
// one object
a : { b : 1, c : 2 }
// two objects that are merged via concatenation rules
a : { b : 1 } { c : 2 }
// two fields that are merged
a : { b : 1 }
a : { c : 2 }
Here are several ways to define `a` to the same array value:
// one array
a : [ 1, 2, 3, 4 ]
// two arrays that are concatenated
a : [ 1, 2 ] [ 3, 4 ]
// a later definition referring to an earlier
// (see "self-referential substitutions" below)
a : [ 1, 2 ]
a : ${a} [ 3, 4 ]
A common use of object concatenation is "inheritance":
data-center-generic = { cluster-size = 6 }
data-center-east = ${data-center-generic} { name = "east" }
A common use of array concatenation is to add to paths:
path = [ /bin ]
path = ${path} [ /usr/bin ]
#### Note: Concatenation with whitespace and substitutions
When concatenating substitutions such as `${foo} ${bar}`, the
substitutions may turn out to be strings (which makes the
whitespace between them significant) or may turn out to be objects
or lists (which makes it irrelevant). Unquoted whitespace must be
ignored in between substitutions which resolve to objects or
lists. Quoted whitespace should be an error.
#### Note: Arrays without commas or newlines
Arrays allow you to use newlines instead of commas, but not
whitespace instead of commas. Non-newline whitespace will produce
concatenation rather than separate elements.
// this is an array with one element, the string "1 2 3 4"
[ 1 2 3 4 ]
// this is an array of four integers
[ 1
2
3
4 ]
// an array of one element, the array [ 1, 2, 3, 4 ]
[ [ 1, 2 ] [ 3, 4 ] ]
// an array of two arrays
[ [ 1, 2 ]
[ 3, 4 ] ]
If this gets confusing, just use commas. The concatenation
behavior is useful rather than surprising in cases like:
[ This is an unquoted string my name is ${name}, Hello ${world} ]
[ ${a} ${b}, ${x} ${y} ]
Non-newline whitespace is never an element or field separator.
### Path expressions
Path expressions are used to write out a path through the object
graph. They appear in two places; in substitutions, like
`${foo.bar}`, and as the keys in objects like `{ foo.bar : 42 }`.
Path expressions are syntactically identical to a value
concatenation, except that they may not contain
substitutions. This means that you can't nest substitutions inside
other substitutions, and you can't have substitutions in keys.
When concatenating the path expression, any `.` characters outside
quoted strings are understood as path separators, while inside
quoted strings `.` has no special meaning. So
`foo.bar."hello.world"` would be a path with three elements,
looking up key `foo`, key `bar`, then key `hello.world`.
The main tricky point is that `.` characters in numbers do count
as a path separator. When dealing with a number as part of a path
expression, it's essential to retain the _original_ string
representation of the number as it appeared in the file (rather
than converting it back to a string with a generic
number-to-string library function).
- `10.0foo` is a number then unquoted string `foo` and should
be the two-element path with `10` and `0foo` as the elements.
- `foo10.0` is an unquoted string with a `.` in it, so this would
be a two-element path with `foo10` and `0` as the elements.
- `foo"10.0"` is an unquoted then a quoted string which are
concatenated, so this is a single-element path.
- `1.2.3` is the three-element path with `1`,`2`,`3`
Unlike value concatenations, path expressions are _always_
converted to a string, even if they are just a single value.
If you have an array or element value consisting of the single
value `true`, it's a value concatenation and retains its character
as a boolean value.
If you have a path expression (in a key or substitution) then it
must always be converted to a string, so `true` becomes the string
that would be quoted as `"true"`.
If a path element is an empty string, it must always be quoted.
That is, `a."".b` is a valid path with three elements, and the
middle element is an empty string. But `a..b` is invalid and
should generate an error. Following the same rule, a path that
starts or ends with a `.` is invalid and should generate an error.
### Paths as keys
If a key is a path expression with multiple elements, it is
expanded to create an object for each path element other than the
last. The last path element, combined with the value, becomes a
field in the most-nested object.
In other words:
foo.bar : 42
is equivalent to:
foo { bar : 42 }
and:
foo.bar.baz : 42
is equivalent to:
foo { bar { baz : 42 } }
and so on. These values are merged in the usual way; which implies
that:
a.x : 42, a.y : 43
is equivalent to:
a { x : 42, y : 43 }
Because path expressions work like value concatenations, you can
have whitespace in keys:
a b c : 42
is equivalent to:
"a b c" : 42
Because path expressions are always converted to strings, even
single values that would normally have another type become
strings.
- `true : 42` is `"true" : 42`
- `3 : 42` is `"3" : 42`
- `3.14 : 42` is `"3" : { "14" : 42 }`
As a special rule, the unquoted string `include` may not begin a
path expression in a key, because it has a special interpretation
(see below).
### Substitutions
Substitutions are a way of referring to other parts of the
configuration tree.
The syntax is `${pathexpression}` or `${?pathexpression}` where
the `pathexpression` is a path expression as described above. This
path expression has the same syntax that you could use for an
object key.
The `?` in `${?pathexpression}` must not have whitespace before
it; the three characters `${?` must be exactly like that, grouped
together.
For substitutions which are not found in the configuration tree,
implementations may try to resolve them by looking at system
environment variables or other external sources of configuration.
(More detail on environment variables in a later section.)
Substitutions are not parsed inside quoted strings. To get a
string containing a substitution, you must use value concatenation
with the substitution in the unquoted portion:
key : ${animal.favorite} is my favorite animal
Or you could quote the non-substitution portion:
key : ${animal.favorite}" is my favorite animal"
Substitutions are resolved by looking up the path in the
configuration. The path begins with the root configuration object,
i.e. it is "absolute" rather than "relative."
Substitution processing is performed as the last parsing step, so
a substitution can look forward in the configuration. If a
configuration consists of multiple files, it may even end up
retrieving a value from another file.
If a key has been specified more than once, the substitution will
always evaluate to its latest-assigned value (that is, it will
evaluate to the merged object, or the last non-object value that
was set, in the entire document being parsed including all
included files).
If a configuration sets a value to `null` then it should not be
looked up in the external source. Unfortunately there is no way to
"undo" this in a later configuration file; if you have `{ "HOME" :
null }` in a root object, then `${HOME}` will never look at the
environment variable. There is no equivalent to JavaScript's
`delete` operation in other words.
If a substitution does not match any value present in the
configuration and is not resolved by an external source, then it
is undefined. An undefined substitution with the `${foo}` syntax
is invalid and should generate an error.
If a substitution with the `${?foo}` syntax is undefined:
- if it is the value of an object field then the field should not
be created. If the field would have overridden a previously-set
value for the same field, then the previous value remains.
- if it is an array element then the element should not be added.
- if it is part of a value concatenation with another string then
it should become an empty string; if part of a value
concatenation with an object or array it should become an empty
object or array.
- `foo : ${?bar}` would avoid creating field `foo` if `bar` is
undefined. `foo : ${?bar}${?baz}` would also avoid creating the
field if _both_ `bar` and `baz` are undefined.
Substitutions are only allowed in field values and array
elements (value concatenations), they are not allowed in keys or
nested inside other substitutions (path expressions).
A substitution is replaced with any value type (number, object,
string, array, true, false, null). If the substitution is the only
part of a value, then the type is preserved. Otherwise, it is
value-concatenated to form a string.
#### Self-Referential Substitutions
The big picture:
- substitutions normally "look forward" and use the final value
for their path expression
- when this would create a cycle, when possible the cycle must be
broken by looking backward only (thus removing one of the
substitutions that's a link in the cycle)
The idea is to allow a new value for a field to be based on the
older value:
path : "a:b:c"
path : ${path}":d"
A _self-referential field_ is one which:
- has a substitution, or value concatenation containing a
substitution, as its value
- where this field value refers to the field being defined,
either directly or by referring to one or more other
substitutions which eventually point back to the field being
defined
Examples of self-referential fields:
- `a : ${a}`
- `a : ${a}bc`
- `path : ${path} [ /usr/bin ]`
Note that an object or array with a substitution inside it is
_not_ considered self-referential for this purpose. The
self-referential rules do _not_ apply to:
- `a : { b : ${a} }`
- `a : [${a}]`
These cases are unbreakable cycles that generate an error. (If
"looking backward" were allowed for these, something like
`a={ x : 42, y : ${a.x} }` would look backward for a
nonexistent `a` while resolving `${a.x}`.)
A possible implementation is:
- substitutions are resolved by looking up paths in a document.
Cycles only arise when the lookup document is an ancestor
node of the substitution node.
- while resolving a potentially self-referential field (any
substitution or value concatenation that contains a
substitution), remove that field and all fields which override
it from the lookup document.
The simplest form of this implementation will report a circular
reference as missing; in `a : ${a}` you would remove `a : ${a}`
while resolving `${a}`, leaving an empty document to look up
`${a}` in. You can give a more helpful error message if, rather
than simply removing the field, you leave a marker value
describing the cycle. Then generate an error if you return to that
marker value during resolution.
Cycles should be treated the same as a missing value when
resolving an optional substitution (i.e. the `${?foo}` syntax).
If `${?foo}` refers to itself then it's as if it referred to a
nonexistent value.
#### The `+=` field separator
Fields may have `+=` as a separator rather than `:` or `=`. A
field with `+=` transforms into a self-referential array
concatenation, like this:
a += b
becomes:
a = ${?a} [b]
`+=` appends an element to a previous array. If the previous value
was not an array, an error will result just as it would in the
long form `a = ${?a} [b]`. Note that the previous value is
optional (`${?a}` not `${a}`), which allows `a += b` to be the
first mention of `a` in the file (it is not necessary to have `a =
[]` first).
Note: Akka 2.0 (and thus Play 2.0) contains an embedded
implementation of the config lib which does not support `+=`.
#### Examples of Self-Referential Substitutions
In isolation (with no merges involved), a self-referential field
is an error because the substitution cannot be resolved:
foo : ${foo} // an error
When `foo : ${foo}` is merged with an earlier value for `foo`,
however, the substitution can be resolved to that earlier value.
When merging two objects, the self-reference in the overriding
field refers to the overridden field. Say you have:
foo : { a : 1 }
and then:
foo : ${foo}
Then `${foo}` resolves to `{ a : 1 }`, the value of the overridden
field.
It would be an error if these two fields were reversed, so first:
foo : ${foo}
and then second:
foo : { a : 1 }
Here the `${foo}` self-reference comes before `foo` has a value,
so it is undefined, exactly as if the substitution referenced a
path not found in the document.
Because `foo : ${foo}` conceptually looks to previous definitions
of `foo` for a value, the error should be treated as "undefined"
rather than "intractable cycle"; as a result, the optional
substitution syntax `${?foo}` does not create a cycle:
foo : ${?foo} // this field just disappears silently
If a substitution is hidden by a value that could not be merged
with it (by a non-object value) then it is never evaluated and no
error will be reported. So for example:
foo : ${does-not-exist}
foo : 42
In this case, no matter what `${does-not-exist}` resolves to, we
know `foo` is `42`, so `${does-not-exist}` is never evaluated and
there is no error. The same is true for cycles like `foo : ${foo},
foo : 42`, where the initial self-reference must simply be ignored.
A self-reference resolves to the value "below" even if it's part
of a path expression. So for example:
foo : { a : { c : 1 } }
foo : ${foo.a}
foo : { a : 2 }
Here, `${foo.a}` would refer to `{ c : 1 }` rather than `2` and so
the final merge would be `{ a : 2, c : 1 }`.
Recall that for a field to be self-referential, it must have a
substitution or value concatenation as its value. If a field has
an object or array value, for example, then it is not
self-referential even if there is a reference to the field itself
inside that object or array.
Implementations must be careful to allow objects to refer to paths
within themselves, for example:
bar : { foo : 42,
baz : ${bar.foo}
}
Here, if an implementation resolved all substitutions in `bar` as
part of resolving the substitution `${bar.foo}`, there would be a
cycle. The implementation must only resolve the `foo` field in
`bar`, rather than recursing the entire `bar` object.
Because there is no inherent cycle here, the substitution must
"look forward" (including looking at the field currently being
defined). To make this clearer, `bar.baz` would be `43` in:
bar : { foo : 42,
baz : ${bar.foo}
}
bar : { foo : 43 }
Mutually-referring objects should also work, and are not
self-referential (so they look forward):
// bar.a should end up as 4
bar : { a : ${foo.d}, b : 1 }
bar.b = 3
// foo.c should end up as 3
foo : { c : ${bar.b}, d : 2 }
foo.d = 4
Another tricky case is an optional self-reference in a value
concatenation, in this example `a` should be `foo` not `foofoo`
because the self reference has to "look back" to an undefined `a`:
a = ${?a}foo
In general, in resolving a substitution the implementation must:
- lazy-evaluate the substitution target so there's no
"circularity by side effect"
- "look forward" and use the final value for the path
specified in the substitution
- if a cycle results, the implementation must "look back"
in the merge stack to try to resolve the cycle
- if neither lazy evaluation nor "looking only backward" resolves
a cycle, the substitution is missing which is an error unless
the `${?foo}` optional-substitution syntax was used.
For example, this is not possible to resolve:
bar : ${foo}
foo : ${bar}
A multi-step loop like this should also be detected as invalid:
a : ${b}
b : ${c}
c : ${a}
Some cases have undefined behavior because the behavior depends on
the order in which two fields are resolved, and that order is not
defined. For example:
a : 1
b : 2
a : ${b}
b : ${a}
Implementations are allowed to handle this by setting both `a` and
`b` to 1, setting both to `2`, or generating an error. Ideally
this situation would generate an error, but that may be difficult
to implement. Making the behavior defined would require always
working with ordered maps rather than unordered maps, which is too
constraining. Implementations only have to track order for
duplicate instances of the same field (i.e. merges).
Implementations must set both `a` and `b` to the same value in
this case, however. In practice this means that all substitutions
must be memoized (resolved once, with the result
retained). Memoization should be keyed by the substitution
"instance" (the specific occurrence of the `${}` expression)
rather than by the path inside the `${}` expression, because
substitutions may be resolved differently depending on their
position in the file.
### Includes
#### Include syntax
An _include statement_ consists of the unquoted string `include`
followed by whitespace and then either:
- a single _quoted_ string which is interpreted heuristically as
URL, filename, or classpath resource.
- `url()`, `file()`, or `classpath()` surrounding a quoted string
which is then interpreted as a URL, file, or classpath. The
string must be quoted, unlike in CSS.
- `required()` surrounding one of the above
An include statement can appear in place of an object field.
If the unquoted string `include` appears at the start of a path
expression where an object key would be expected, then it is not
interpreted as a path expression or a key.
Instead, the next value must be a _quoted_ string or a quoted
string surrounded by `url()`, `file()`, or `classpath()`.
This value is the _resource name_.
Together, the unquoted `include` and the resource name substitute
for an object field syntactically, and are separated from the
following object fields or includes by the usual comma (and as
usual the comma may be omitted if there's a newline).
If an unquoted `include` at the start of a key is followed by
anything other than a single quoted string or the
`url("")`/`file("")`/`classpath("")` syntax, it is invalid and an
error should be generated.
There can be any amount of whitespace, including newlines, between
the unquoted `include` and the resource name. For `url()` etc.,
whitespace is allowed inside the parentheses `()` (outside of the
quotes).
Value concatenation is NOT performed on the "argument" to
`include` or `url()` etc. The argument must be a single quoted
string. No substitutions are allowed, and the argument may not be
an unquoted string or any other kind of value.
Unquoted `include` has no special meaning if it is not the start
of a key's path expression.
It may appear later in the key:
# this is valid
{ foo include : 42 }
# equivalent to
{ "foo include" : 42 }
It may appear as an object or array value:
{ foo : include } # value is the string "include"
[ include ] # array of one string "include"
You can quote `"include"` if you want a key that starts with the
word `"include"`, only unquoted `include` is special:
{ "include" : 42 }
Note: Akka 2.0 (and thus Play 2.0) contains an embedded
implementation of the config lib which does not support the
`url()`/`file()`/`classpath()` syntax. Only the heuristic `include
"foo"` syntax is supported in that version.
#### Include semantics: merging
An _including file_ contains the include statement and an
_included file_ is the one specified in the include statement.
(They need not be regular files on a filesystem, but assume they
are for the moment.)
An included file must contain an object, not an array. This is
significant because both JSON and HOCON allow arrays as root
values in a document.
If an included file contains an array as the root value, it is
invalid and an error should be generated.
The included file should be parsed, producing a root object. The
keys from the root object are conceptually substituted for the
include statement in the including file.
- If a key in the included object occurred prior to the include
statement in the including object, the included key's value
overrides or merges with the earlier value, exactly as with
duplicate keys found in a single file.
- If the including file repeats a key from an earlier-included
object, the including file's value would override or merge
with the one from the included file.
#### Include semantics: substitution
Substitutions in included files are looked up at two different
paths; first, relative to the root of the included file; second,
relative to the root of the including configuration.
Recall that substitution happens as a final step, _after_
parsing. It should be done for the entire app's configuration, not
for single files in isolation.
Therefore, if an included file contains substitutions, they must
be "fixed up" to be relative to the app's configuration root.
Say for example that the root configuration is this:
{ a : { include "foo.conf" } }
And "foo.conf" might look like this:
{ x : 10, y : ${x} }
If you parsed "foo.conf" in isolation, then `${x}` would evaluate
to 10, the value at the path `x`. If you include "foo.conf" in an
object at key `a`, however, then it must be fixed up to be
`${a.x}` rather than `${x}`.
Say that the root configuration redefines `a.x`, like this:
{
a : { include "foo.conf" }
a : { x : 42 }
}
Then the `${x}` in "foo.conf", which has been fixed up to
`${a.x}`, would evaluate to `42` rather than to `10`.
Substitution happens _after_ parsing the whole configuration.
However, there are plenty of cases where the included file might
intend to refer to the application's root config. For example, to
get a value from a system property or from the reference
configuration. So it's not enough to only look up the "fixed up"
path, it's necessary to look up the original path as well.
#### Include semantics: missing files and required files
By default, if an included file does not exist then the include statement should
be silently ignored (as if the included file contained only an
empty object).
If however an included resource is mandatory then the name of the
included resource may be wrapped with `required()`, in which case
file parsing will fail with an error if the resource cannot be resolved.
The syntax for this is
include required("foo.conf")
include required(file("foo.conf"))
include required(classpath("foo.conf"))
include required(url("http://localhost/foo.conf"))
Other IO errors probably should not be ignored but implementations
will have to make a judgment which IO errors reflect an ignorable
missing file, and which reflect a problem to bring to the user's
attention.
#### Include semantics: file formats and extensions
Implementations may support including files in other formats.
Those formats must be compatible with the JSON type system, or
have some documented mapping to JSON's type system.
If an implementation supports multiple formats, then the extension
may be omitted from the name of included files:
include "foo"
If a filename has no extension, the implementation should treat it
as a basename and try loading the file with all known extensions.
If the file exists with multiple extensions, they should _all_ be
loaded and merged together.
Files in HOCON format should be parsed last. Files in JSON format
should be parsed next-to-last.
In short, `include "foo"` might be equivalent to:
include "foo.properties"
include "foo.json"
include "foo.conf"
This same extension-based behavior is applied to classpath
resources and files.
For URLs, a basename without extension is not allowed; only the
exact URL specified is used. The format will be chosen based on
the Content-Type if available, or by the extension of the path
component of the URL if no Content-Type is set. This is true even
for file: URLs.
#### Include semantics: locating resources
A quoted string not surrounded by `url()`, `file()`, `classpath()`
must be interpreted heuristically. The heuristic is to treat the
quoted string as:
- a URL, if the quoted string is a valid URL with a known
protocol.
- otherwise, a file or other resource "adjacent to" the one being
parsed and of the same type as the one being parsed. The meaning
of "adjacent to", and the string itself, has to be specified
separately for each kind of resource.
- On the Java Virtual Machine, if an include statement does not
identify a valid URL or an existing resource "adjacent to" the
including resource, implementations may wish to fall back to a
classpath resource. This allows configurations found in files
or URLs to access classpath resources in a natural way.
Implementations may vary in the kinds of resources they can
include.
For resources located on the Java classpath:
- included resources are looked up by calling `getResource()` on
the same class loader used to look up the including resource.
- if the included resource name is absolute (starts with '/')
then it should be passed to `getResource()` with the '/'
removed.
- if the included resource name does not start with '/' then it
should have the "directory" of the including resource
prepended to it, before passing it to `getResource()`. If the
including resource is not absolute (no '/') and has no "parent
directory" (is just a single path element), then the included
relative resource name should be left as-is.
- it would be wrong to use `getResource()` to get a URL and then
locate the included name relative to that URL, because a class
loader is not required to have a one-to-one mapping between
paths in its URLs and the paths it handles in `getResource()`.
In other words, the "adjacent to" computation should be done
on the resource name not on the resource's URL.
For plain files on the filesystem:
- if the included file is an absolute path then it should be kept
absolute and loaded as such.
- if the included file is a relative path, then it should be
located relative to the directory containing the including
file. The current working directory of the process parsing a
file must NOT be used when interpreting included paths.
- if the file is not found, fall back to the classpath resource.
The classpath resource should not have any package name added
in front, it should be relative to the "root"; which means any
leading "/" should just be removed (absolute is the same as
relative since it's root-relative). The "/" is handled for
consistency with including resources from inside other
classpath resources, where the resource name may not be
root-relative and "/" allows specifying relative to root.
URLs:
- for files loaded from a URL, "adjacent to" should be based
on parsing the URL's path component, replacing the last
path element with the included name.
- file: URLs should behave in exactly the same way as a plain
filename
Implementations need not support files, Java resources, or URLs;
and they need not support particular URL protocols. However, if
they do support them they should do so as described above.
Note that at present, if `url()`/`file()`/`classpath()` are
specified, the included items are NOT interpreted relative to the
including items. Relative-to-including-file paths only work with
the heuristic `include "foo.conf"`. This may change in the future.
### Conversion of numerically-indexed objects to arrays
In some file formats and contexts, such as Java properties files,
there isn't a good way to define arrays. To provide some mechanism
for this, implementations should support converting objects with
numeric keys into arrays. For example, this object:
{ "0" : "a", "1" : "b" }
could be treated as:
[ "a", "b" ]
This allows creating an array in a properties file like this:
foo.0 = "a"
foo.1 = "b"
The details:
- the conversion should be done lazily when required to avoid
a type error, NOT eagerly anytime an object has numeric
keys.
- the conversion should be done when you would do an automatic
type conversion (see the section "Automatic type conversions"
below).
- the conversion should be done in a concatenation when a list
is expected and an object with numeric keys is found.
- the conversion should not occur if the object is empty or
has no keys which parse as positive integers.
- the conversion should ignore any keys which do not parse
as positive integers.
- the conversion should sort by the integer value of each
key and then build the array; if the integer keys are "0" and
"2" then the resulting array would have indices "0" and "1",
i.e. missing indices in the object are eliminated.
## MIME Type
Use "application/hocon" for Content-Type.
## API Recommendations
Implementations of HOCON ideally follow certain conventions and
work in a predictable way.
### Automatic type conversions
If an application asks for a value with a particular type, the
implementation should attempt to convert types as follows:
- number to string: convert the number into a string
representation that would be a valid number in JSON.
- boolean to string: should become the string "true" or "false"
- string to number: parse the number with the JSON rules
- string to boolean: the strings "true", "yes", "on", "false",
"no", "off" should be converted to boolean values. It's
tempting to support a long list of other ways to write a
boolean, but for interoperability and keeping it simple, it's
recommended to stick to these six.
- string to null: the string `"null"` should be converted to a
null value if the application specifically asks for a null
value, though there's probably no reason an app would do this.
- numerically-indexed object to array: see the section
"Conversion of numerically-indexed objects to arrays" above
The following type conversions should NOT be performed:
- null to anything: If the application asks for a specific type
and finds null instead, that should usually result in an error.
- object to anything
- array to anything
- anything to object
- anything to array, with the exception of numerically-indexed
object to array
Converting objects and arrays to and from strings is tempting, but
in practical situations raises thorny issues of quoting and
double-escaping.
### Units format
Implementations may wish to support interpreting a value with some
family of units, such as time units or memory size units: `10ms`
or `512K`. HOCON does not have an extensible type system and there
is no way to add a "duration" type. However, for example, if an
application asks for milliseconds, the implementation can try to
interpret a value as a milliseconds value.
If an API supports this, for each family of units it should define
a default unit in the family. For example, the family of duration
units might default to milliseconds (see below for details on
durations). The implementation should then interpret values as
follows:
- if the value is a number, it is taken to be a number in
the default unit.
- if the value is a string, it is taken to be this sequence:
- optional whitespace
- a number
- optional whitespace
- an optional unit name consisting only of letters (letters
are the Unicode `L*` categories, Java `isLetter()`)
- optional whitespace
If a string value has no unit name, then it should be
interpreted with the default unit, as if it were a number. If a
string value has a unit name, that name of course specifies the
value's interpretation.
### Duration format
Implementations may wish to support a `getMilliseconds()` (and
similar for other time units).
This can use the general "units format" described above; bare
numbers are taken to be in milliseconds already, while strings are
parsed as a number plus an optional unit string.
The supported unit strings for duration are case sensitive and
must be lowercase. Exactly these strings are supported:
- `ns`, `nano`, `nanos`, `nanosecond`, `nanoseconds`
- `us`, `micro`, `micros`, `microsecond`, `microseconds`
- `ms`, `milli`, `millis`, `millisecond`, `milliseconds`
- `s`, `second`, `seconds`
- `m`, `minute`, `minutes`
- `h`, `hour`, `hours`
- `d`, `day`, `days`
### Size in bytes format
Implementations may wish to support a `getBytes()` returning a
size in bytes.
This can use the general "units format" described above; bare
numbers are taken to be in bytes already, while strings are
parsed as a number plus an optional unit string.
The one-letter unit strings may be uppercase (note: duration units
are always lowercase, so this convention is specific to size
units).
There is an unfortunate nightmare with size-in-bytes units, that
they may be in powers or two or powers of ten. The approach
defined by standards bodies appears to differ from common usage,
such that following the standard leads to people being confused.
Worse, common usage varies based on whether people are talking
about RAM or disk sizes, and various existing operating systems
and apps do all kinds of different things. See
http://en.wikipedia.org/wiki/Binary_prefix#Deviation_between_powers_of_1024_and_powers_of_1000
for examples. It appears impossible to sort this out without
causing confusion for someone sometime.
For single bytes, exactly these strings are supported:
- `B`, `b`, `byte`, `bytes`
For powers of ten, exactly these strings are supported:
- `kB`, `kilobyte`, `kilobytes`
- `MB`, `megabyte`, `megabytes`
- `GB`, `gigabyte`, `gigabytes`
- `TB`, `terabyte`, `terabytes`
- `PB`, `petabyte`, `petabytes`
- `EB`, `exabyte`, `exabytes`
- `ZB`, `zettabyte`, `zettabytes`
- `YB`, `yottabyte`, `yottabytes`
For powers of two, exactly these strings are supported:
- `K`, `k`, `Ki`, `KiB`, `kibibyte`, `kibibytes`
- `M`, `m`, `Mi`, `MiB`, `mebibyte`, `mebibytes`
- `G`, `g`, `Gi`, `GiB`, `gibibyte`, `gibibytes`
- `T`, `t`, `Ti`, `TiB`, `tebibyte`, `tebibytes`
- `P`, `p`, `Pi`, `PiB`, `pebibyte`, `pebibytes`
- `E`, `e`, `Ei`, `EiB`, `exbibyte`, `exbibytes`
- `Z`, `z`, `Zi`, `ZiB`, `zebibyte`, `zebibytes`
- `Y`, `y`, `Yi`, `YiB`, `yobibyte`, `yobibytes`
It's very unclear which units the single-character abbreviations
("128K") should go with; some precedents such as `java -Xmx 2G`
and the GNU tools such as `ls` map these to powers of two, so this
spec copies that. You can certainly find examples of mapping these
to powers of ten, though. If you don't like ambiguity, don't use
the single-letter abbreviations.
Note: any value in zetta/zebi or yotta/yobi will overflow a 64-bit
integer, and of course large-enough values in any of the units may
overflow. Most real-world APIs and apps will not support byte
counts that overflow a 64-bit integer. The huge units are provided
just to be complete but probably aren't useful in practice. At
least not in 2014.
### Config object merging and file merging
It may be useful to offer a method to merge two objects. If such a
method is provided, it should work as if the two objects were
duplicate values for the same key in the same file. (See the
section earlier on duplicate key handling.)
As with duplicate keys, an intermediate non-object value "hides"
earlier object values. So say you merge three objects in this
order:
- `{ a : { x : 1 } }` (first priority)
- `{ a : 42 }` (fallback)
- `{ a : { y : 2 } }` (another fallback)
The result would be `{ a : { x : 1 } }`. The two objects are not
merged because they are not "adjacent"; the merging is done in
pairs, and when `42` is paired with `{ y : 2 }`, `42` simply wins
and loses all information about what it overrode.
But if you re-ordered like this:
- `{ a : { x : 1 } }` (first priority)
- `{ a : { y : 2 } }` (fallback)
- `{ a : 42 }` (another fallback)
Now the result would be `{ a : { x : 1, y : 2 } }` because the two
objects are adjacent.
This rule for merging objects loaded from different files is
_exactly_ the same behavior as for merging duplicate fields in the
same file. All merging works the same way.
Needless to say, normally it's well-defined whether a config
setting is supposed to be a number or an object. This kind of
weird pathology where the two are mixed should not be happening.
The one place where it matters, though, is that it allows you to
"clear" an object and start over by setting it to null and then
setting it back to a new object. So this behavior gives people a
way to get rid of default fallback values they don't want.
### Java properties mapping
It may be useful to merge Java properties data with data loaded
from JSON or HOCON. See the Java properties spec here:
http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29
Java properties parse as a one-level map from string keys to
string values.
To convert to HOCON, first split each key on the `.` character,
keeping any empty strings (including leading and trailing empty
strings). Note that this is _very different_ from parsing a path
expression.
The key split on `.` is a series of path elements. So the
properties key with just `.` is a path with two elements, both of
them an empty string. `a.` is a path with two elements, `a` and
empty string. (Java's `String.split()` does NOT do what you want
for this.)
It is impossible to represent a key with a `.` in it in a
properties file. If a JSON/HOCON key has a `.` in it, which is
possible if the key is quoted, then there is no way to refer to it
as a Java property. It is not recommended to name HOCON keys with
a `.` in them, since it would be confusing at best in any case.
Once you have a path for each value, construct a tree of
JSON-style objects with the string value of each property located
at that value's path.
Values from properties files are _always_ strings, even if they
could be parsed as some other type. Implementations should do type
conversion if an app asks for an integer, as described in an
earlier section.
When Java loads a properties file, unfortunately it does not
preserve the order of the file. As a result, there is an
intractable case where a single key needs to refer to both a
parent object and a string value. For example, say the Java
properties file has:
a=hello
a.b=world
In this case, `a` needs to be both an object and a string value.
The _object_ must always win in this case... the "object wins"
rule throws out at most one value (the string) while "string wins"
would throw out all values in the object. Unfortunately, when
properties files are mapped to the JSON structure, there is no way
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 and wouldn't help
with system properties anyway.
### Conventional configuration files for JVM apps
By convention, JVM apps have two parts to their configuration:
- 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.
- the reference config may be different for different class
loaders, since each jar may provide a `reference.conf`
to go with the code in that jar.
- a single JVM may have multiple application configs if
it has multiple modules or contexts of some kind.
The reference config for a given class loader should be merged and
resolved first, and may be shared among all application configs in
that class loader. Substitutions in the reference config are not
affected by any application configs, because the reference config
should be resolved by itself.
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_
settings found in the configuration file. This supports specifying
config options on the command line.
### Substitution fallback to environment variables
Recall that if a substitution is not present (not even set to
`null`) within a configuration tree, implementations may search
for it from external sources. One such source could be environment
variables.
It's recommended that HOCON keys always use lowercase, because
environment variables generally are capitalized. This avoids
naming collisions between environment variables and configuration
properties. (While on Windows getenv() is generally not
case-sensitive, the lookup will be case sensitive all the way
until the env variable fallback lookup is reached).
See also the notes below on Windows and case sensitivity.
An application can explicitly block looking up a substitution in
the environment by setting a value in the configuration, with the
same name as the environment variable. You could set `HOME : null`
in your root object to avoid expanding `${HOME}` from the
environment, for example.
Environment variables are interpreted as follows:
- env variables set to the empty string are kept as such (set to
empty string, rather than undefined)
- System.getenv throws SecurityException: treated as not present
- encoding is handled by Java (System.getenv already returns
a Unicode string)
- environment variables always become a string value, though
if an app asks for another type automatic type conversion
would kick in
### hyphen-separated vs. camelCase
Config keys are encouraged to be `hyphen-separated` rather than
`camelCase`.
## Note on Java properties similarity
You can write a HOCON file that looks much like a Java properties
file, and many valid Java properties files will also parse as
HOCON.
However, HOCON is not a Java properties superset and the corner
cases work like JSON, not like properties.
Differences include but are probably not limited to:
- certain characters that can be unquoted in properties files
have to be placed in JSON-style double-quoted strings in HOCON
- unquoted strings in HOCON do not support escape sequences
- unquoted strings in HOCON do not preserve trailing whitespace
- multi-line unquoted strings using backslash to continue the
line are not allowed in HOCON
- in properties files you can omit the value for a key and it's
interpreted as an empty string, in HOCON you cannot omit the
value
- properties files support '!' as a comment character
- HOCON allows comments on the same line as a key or value, while
properties files only recognize comment characters if they
occur as the first character on the line
- HOCON interprets `${}` as a substitution
## Note on Windows and case sensitivity of environment variables
HOCON's lookup of environment variable values is always case sensitive, but
Linux and Windows differ in their handling of case.
Linux allows one to define multiple environment variables with the same
name but with different case; so both "PATH" and "Path" may be defined
simultaneously. HOCON's access to these environment variables on Linux
is straightforward; ie just make sure you define all your vars with the required case.
Windows is more confusing. Windows environment variables names may contain a
mix of upper and lowercase characters, eg "Path", however Windows does not
allow one to define multiple instances of the same name but differing in case.
Whilst accessing env vars in Windows is case insensitive, accessing env vars in
HOCON is case sensitive.
So if you know that you HOCON needs "PATH" then you must ensure that
the variable is defined as "PATH" rather than some other name such as
"Path" or "path".
However, Windows does not allow us to change the case of an existing env var; we can't
simply redefine the var with an upper case name.
The only way to ensure that your environment variables have the desired case
is to first undefine all the env vars that you will depend on then redefine
them with the required case.
For example, the the ambient environment might have this definition ...
```
set Path=A;B;C
```
.. we just don't know. But if the HOCON needs "PATH", then the start script must
take a precautionary approach and enforce the necessary case as follows ...
```
set OLDPATH=%PATH%
set PATH=
set PATH=%OLDPATH%
%JAVA_HOME%/bin/java ....
```
You cannot know what ambient environment variables might exist in the ambient environment
when your program is invoked, nor what case those definitions might have.
Therefore the only safe thing to do is redefine all the vars you rely on as shown above.
config-1.3.1/LICENSE-2.0.txt 0000664 0000000 0000000 00000026136 12771472746 0015132 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
config-1.3.1/NEWS.md 0000664 0000000 0000000 00000037020 12771472746 0014102 0 ustar 00root root 0000000 0000000 # 1.3.1: September 24, 2016
- added `include required("foo")` syntax to specify includes that
fail if not present
- support for more kinds of property in ConfigBeanFactory:
* enumerations
* optional fields
* lists of beans
- numbers can now start with a decimal (".33" vs. "0.33"), these
are still parsed as strings but Config.getDouble() etc. will
convert them to numbers
- a few other small bugfixes
# 1.3.0: May 8, 2015
- no changes from 1.3.0-M3
- this is an ABI-guaranteed stable release.
- 1.3.0 should be ABI-compatible with 1.2.x for most applications,
though there are enough changes to corner cases and
implementation that some obscure things could break. Please see
the notes for the 1.3.0 milestones below for details, especially
for 1.3.0-M1.
# 1.3.0-M3: April 21, 2015
- this is an ABI-not-guaranteed beta release in advance
of 1.3.0. Please see the notes for 1.3.0-M1 below for warnings,
caveats, and the bulk of what's changed since 1.2.1.
API changes (since 1.3.0-M2):
- renamed some methods in the new ConfigDocument for consistency
with Config (breaks ABI vs. 1.3.0-M2, but not vs. any stable
release)
Fixes:
- couple of bugfixes in ConfigDocument
Thank you to contributors with commits since v1.3.0-M2 tag:
- Preben Ingvaldsen
# 1.3.0-M2: April 1, 2015
- not in fact an April Fool's joke. Unless it's broken. Then it
was.
- this is an ABI-not-guaranteed beta release in advance
of 1.3.0. Please see the notes for 1.3.0-M1 below for warnings,
caveats, and the bulk of what's changed since 1.2.1.
- this release churns the internals a good bit since 1.3.0-M1,
so would benefit from your testing efforts.
New API (since 1.3.0-M1):
- added Config.hasPathOrNull
- added Config.getIsNull
- added parser.ConfigDocument which supports simple load/edit/save
on a config file. For now, the only allowed edits are
removing/replacing values. This was a major effort (redoing the
whole parser), implemented by Preben Ingvaldsen.
Fixes:
- added missing @since tags to javadoc
- fixed obscure bug in converting to camel case when instantiating
beans
Thank you to contributors with commits since v1.3.0-M1 tag:
- Glen Ford
- Jay McCure
- Preben Ingvaldsen
# 1.3.0-M1: March 6, 2015
- this is an ABI-not-guaranteed beta release in advance
of 1.3.0. The public, documented ABI should not be broken
vs. 1.2.0; however, there are enough changes that something
certainly could break, and some obscure corner cases have
changed semantics and that could bite some people.
Changes that are most likely to break something:
- now built with Java 8 and requires Java 8
- if you were relying on the order of key iteration in a config,
note that Java 8 changed the iteration order for hashes
and that includes `Config` and `ConfigObject`
- several correctness fixes to resolving substitutions (the
`${foo}` syntax). These should only matter in weird corner
cases, but some people did encounter the problems such as in
#177.
- throw an exception if a size-in-bytes values are out of Long
range #170
- two adjacent undefined concatenation elements (like
`${?foo}${?bar}`) now become undefined instead of throwing an
exception
- when rendering a path that starts with a digit, don't put
quotes around it
- set the Accept header on http requests when loading
config from a URL
- when getting a 404 from a URL, treat it as a missing file
(silently ignore) instead of throwing exception.
Other error codes will still throw an exception.
- ConfigParseOptions.prependIncluder/appendIncluder always
throw when given a null includer, formerly they only
threw sometimes
New API:
- `ConfigBeanFactory` will auto-fill a JavaBean from
a `Config`
- it is now possible to create a `ConfigOrigin` using
`ConfigOriginFactory` and to modify origins on values
using `ConfigValue.withOrigin`
- `Config.getMemorySize` returns a `ConfigMemorySize`
- `Config.getDuration` returns a `java.time.Duration`
- the existing `ConfigValueFactory.fromAnyRef` and related
methods now pass through a `ConfigValue` instead of throwing
an exception
- `ConfigFactory.defaultApplication()` returns the default
`Config` used by `ConfigFactory.load()` in between
`defaultReference()` and `defaultOverrides()`, leaving
`ConfigFactory.load()` as a trivial convenience API
that uses no internal magic.
Improvements:
- allow duration abbreviations "nanos", "millis", "micros"
- Config.hasPath is now _much_ faster, so if you were caching to
avoid this you may be able to stop
- new debug option -Dconfig.trace=substitutions explains
how `${foo}` references are being resolved
- sort numeric keys in numeric order when rendering
- allow whitespace in between two substitutions referring to
objects or lists when concatenating them, so `${foo} ${bar}`
and `${foo}${bar}` are now the same if foo and bar are objects
or lists.
- better error messages for problems loading resources from
classpath, now we show the jar URL that failed
- even more test coverage!
- lots of minor javadoc fixes
- method names in javadoc now link to github source
Bug fixes:
- fix "allow unresolved" behavior for unresolved list elements
- class loaders are cached with a WeakReference to avoid leaks
#171
- create valid output for values with multiline descriptions
#239
- `-Dsun.io.serialization.extendedDebugInfo=true` no longer
explodes due to calling toString on an internal object,
#176
Thank you to contributors with commits since v1.2.1 tag:
- Alex Wei
- Andrey Zaytsev
- Ben Jackman
- Ben McCann
- Chris Martin
- Dale Wijnand
- Francois Dang Ngoc
- ian
- KAWACHI Takashi
- Kornel Kielczewski
- Lunfu Zhong
- Michel Daviot
- Paul Phillips
- Pavel Yakunin
- Preben Ingvaldsen
- verbeto
- Wu Zhenwei
# 1.2.1: May 2, 2014
- bugfix release, no API additions or changes
- fix resolving substitutions in include statements nested inside
objects
- when rendering an object to a string, sort the fields
- handle unresolved substitutions in value concatenations
- make ConfigOrigin.comments unmodifiable
- when using '+=' or 'include' inside a list, throw an exception
instead of generating a wrong result
- when context class loader is unset throw a more helpful
exception than NullPointerException
- ignore non-string values in a Properties object
# 1.2.0: January 15, 2014
- new stable ABI release (binary compatible with 1.0.x; a few new APIs)
- new API ConfigResolveOptions.setAllowUnresolved lets you
partially-resolve a Config
- new API Config.isResolved lets you check on resolution status
- new API Config.resolveWith lets you source substitutions from
somewhere other than the Config itself
- new API Config.getDuration() replaces getMilliseconds and
getNanoseconds
- if -Dconfig.file, -Dconfig.resource, -Dconfig.url refer to
a nonexistent file, resource, or url it is now an error rather
than silently loading an empty configuration.
- quite a few bugfixes
# 1.1.0-4dd6c85cab1ef1a4415abb74704d60e57497b7b8: January 8, 2014
- remove junk in POM caused by broken local configuration
- build jar using Java 1.6 (and enforce this in build)
- change getDuration to return unboxed long instead of boxed
- API documentation improvements
http://typesafehub.github.io/config/latest/api/
# 1.1.0-9f31d6308e7ebbc3d7904b64ebb9f61f7e22a968: January 6, 2014
- this is a snapshot/preview with API/ABI additions. *New* API
since 1.0.x is NOT guaranteed to remain compatible for now
since the purpose of this release is to test it.
This release is supposed to be ABI-compatible with 1.0.x
however.
- snapshots now use the git hash they are based on in the
version, instead of SNAPSHOT, so they are a stable reference
if you want to test them out.
- if -Dconfig.file, -Dconfig.resource, -Dconfig.url refer to
a nonexistent file, resource, or url it is now an error rather
than silently loading an empty configuration.
- new API Config.getDuration() replaces getMilliseconds and
getNanoseconds. (should it return `long` instead of `Long` even
though it's been in git for a while? weigh in at
https://github.com/typesafehub/config/issues/119 )
- new API ConfigResolveOptions.setAllowUnresolved lets you
partially-resolve a Config
- new API Config.isResolved lets you check on resolution status
- new API Config.resolveWith lets you source substitutions from
somewhere other than the Config itself
- compiled with debug symbols
- add -Dconfig.trace=loads feature to trace loaded files and
failures
- improvements to ConfigObject render() formatting so you can
print out a config in a prettier way
- attempt to honor Content-Type when loading from a URL
- a fair list of corner case bugfixes
# 1.0.2: July 3, 2013
- ignore byte-order mark (BOM), treating it as whitespace
- very minor docs/build improvements
# 1.0.1: May 19, 2013
- when an array is requested and an object found, try to convert
the object to an array if the object has numeric keys in it.
This is intended to support `-Dfoo.0=bar, -Dfoo.1=baz` which
would create `foo : { "0" : "bar", "1" : "baz" }`; which in
turn could now be treated as if it were `foo :
["bar","baz"]`. This is useful for creating array values on the
command line using Java system properties.
- fix a ConcurrentModificationException if an app modified
system properties while we were trying to parse them.
- fix line numbering in error messages for newlines within triple
quotes.
# 1.0.0: October 15, 2012
- no changes from 0.6.0. ABI now guaranteed for 1.0.x series.
# 0.6.0: October 10, 2012
- add ConfigRenderOptions.setJson which can be used to enable or
disable the use of HOCON extensions (other than comments, which
remain separately-controlled). Right now setJson(false) will
result in a somewhat prettier rendering using extensions.
- add ConfigFactory.invalidateCaches() to support reloading
system properties (mostly this is intended for use in
unit tests).
- make ConfigException serializable, in case you have some
logging system or similar that relies on that. Serialization
of ConfigException is not guaranteed to be compatible across
releases.
# 0.5.2: September 6, 2012
- add versions of ConfigFactory.load() which let you specify
ConfigParseOptions and ConfigResolveOptions.
# 0.5.0: July 5, 2012
- triple-quoted strings as in Python or Scala
- obscure backward incompatibilities:
- `""""` previously parsed as two empty strings concatenated
into a single empty string, now it parses as an unterminated
triple-quoted string.
- a triple-quoted string like `"""\n"""` previously parsed as
an empty string, a string with one newline character, and
another empty string, all concatenated into a single
string. Now it parses as a string with two characters
(backslash and lowercase "n").
- in short you could have two adjacent quoted strings before,
where one was an empty string, and now you can't. As far as
I know, the empty string was always worthless in this case
and can just be removed.
- added methods atPath() and atKey() to ConfigValue, to wrap
the value into a Config
- added method withValue() to Config and ConfigObject,
to add a value at a given path or key
# 0.4.1: May 22, 2012
- publish as OSGi bundle
# 0.4.0: April 12, 2012
- this is **rolling toward 1.0** and should be pretty much
feature-complete.
- this version is published on **Maven central** so you need
to update your dependency coordinates
- the **serialization format has changed** to one that's
extensible and lets the library evolve without breaking
serialization all the time. The new format is also much more
compact. However, this change is incompatible with old
serializations, if you rely on that. The hope is to avoid
serialization breakage in the future now that the format is not
the default Java one (which was a direct dump of all the
implementation details).
- **serializing an unresolved Config** (one that hasn't had
resolve() called on it) is no longer supported, you will get
NotSerializableException if you try.
- ConfigValue.render() now supports ConfigRenderOptions which
means you can get a **no-whitespace no-comments plain JSON
rendering** of a ConfigValue
- supports **self-referential substitutions**, such as
`path=${path}":/bin"`, by "looking backward" to the previous
value of `path`
- supports **concatenating arrays and merging objects within a
single value**. So you can do `path=${path} [ "/bin" ]` for
example. See README and spec for more details.
- supports **array append** `+=` where `path+="/bin"` expands to
`path=${?path} [ "/bin" ]`
- supports **specifying type of include** `include
url("http://example.com/")`, `include file("/my/file.conf")`,
and `include classpath("whatever")`. This syntax forces
treatment as URL, file, or classpath resource.
- supports **including URLs** `include
"http://example.com/whatever.conf"` (if an include is a valid
URL, it's loaded as such). This is incompatible with prior
versions, if you have a filename that is also a valid URL, it
would have loaded previously but now it will not. Use the
`include file("")` syntax to force treatment as a file.
- **class loaders are now recursively inherited** through include
statements; previously, even if you set a custom class loader
when parsing a file, it would not be used for parsing a
classpath resource included from the file.
- parseString() and parseReader() now support include statements
in the parsed string or reader
- in -Dconfig.resource=name, name can start with a "/" or not,
doesn't matter
- if you implement ConfigIncluder, you should most likely also
implement ConfigIncluderFile, ConfigIncluderURL, and
ConfigIncluderClasspath. You should also use
ConfigIncludeContext.parseOptions() if appropriate.
- cycles in include statements (self-includes) are now detected
and result in a nicer error instead of stack overflow
- since 0.3.0, there is an obscure incompatible semantic change
in that self-referential substitutions where the cycle could
be broken by partially resolving the object now "look backward"
and may fail to resolve. This is not incompatible with the
version included in Play/Akka 2.0 because in that version this
obscure case just threw an exception. But in 0.3.0 there
were cases that worked that now work differently. You are very
unlikely to be affected by this.
- Play/Akka 2.0 do not and will not have the new stuff in this
version due to the serialization break, they will update
next time they bump their ABI.
# 0.3.1: August 6, 2012
- 0.3.1 is a backport of the "-Dconfig.resource=name can start with a
/" fix to 0.3.0
- 0.3.1 was published on Maven Central while 0.3.0 and earlier
are only on Typesafe's repository
- 0.3.1 is mostly intended for use by Akka 2.0.x (and therefore
Play 2.0.x), everyone else should use a higher version number
# 0.3.0: March 1, 2012
- ConfigFactory methods now use the thread's context class loader
by default, and have overloads so you can specify a class
loader. Because jars may come with "reference.conf" in the jar,
a config must always be loaded with the same class loader as
the jar using the config.
- ConfigValue instances are now serializable
- new methods ConfigObject.withoutKey, ConfigObject.withOnlyKey,
Config.withoutPath, Config.withOnlyPath allow subsetting
configs more easily.
- better handle complex interdependent substitutions (the
`${foo}` syntax) without getting confused; just about anything
that makes conceptual sense should now work. Only inherently
circular config files should fail.
- some minor documentation fixes.
config-1.3.1/README.md 0000664 0000000 0000000 00000077546 12771472746 0014304 0 ustar 00root root 0000000 0000000 Configuration library for JVM languages.
[](https://maven-badges.herokuapp.com/maven-central/com.typesafe/config)
[](https://travis-ci.org/typesafehub/config)
If you have questions or are working on a pull request or just
curious, please feel welcome to join the chat room:
[](https://gitter.im/typesafehub/config?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Overview
- implemented in plain Java with no dependencies
- supports files in three formats: Java properties, JSON, and a
human-friendly JSON superset
- merges multiple files across all formats
- can load from files, URLs, or classpath
- good support for "nesting" (treat any subtree of the config the
same as the whole config)
- users can override the config with Java system properties,
`java -Dmyapp.foo.bar=10`
- supports configuring an app, with its framework and libraries,
all from a single file such as `application.conf`
- parses duration and size settings, "512k" or "10 seconds"
- converts types, so if you ask for a boolean and the value
is the string "yes", or you ask for a float and the value is
an int, it will figure it out.
- JSON superset features:
- comments
- includes
- substitutions (`"foo" : ${bar}`, `"foo" : Hello ${who}`)
- properties-like notation (`a.b=c`)
- less noisy, more lenient syntax
- substitute environment variables (`logdir=${HOME}/logs`)
- API based on immutable `Config` instances, for thread safety
and easy reasoning about config transformations
- extensive test coverage
This library limits itself to config files. If you want to load
config from a database or something, you would need to write some
custom code. The library has nice support for merging
configurations so if you build one from a custom source it's easy
to merge it in.
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Essential Information](#essential-information)
- [License](#license)
- [Binary Releases](#binary-releases)
- [Release Notes](#release-notes)
- [API docs](#api-docs)
- [Bugs and Patches](#bugs-and-patches)
- [Build](#build)
- [Using the Library](#using-the-library)
- [API Example](#api-example)
- [Longer Examples](#longer-examples)
- [Immutability](#immutability)
- [Schemas and Validation](#schemas-and-validation)
- [Standard behavior](#standard-behavior)
- [Note about resolving substitutions in `reference.conf` and `application.conf`](#note-about-resolving-substitutions-in-referenceconf-and-applicationconf)
- [Merging config trees](#merging-config-trees)
- [How to handle defaults](#how-to-handle-defaults)
- [Understanding `Config` and `ConfigObject`](#understanding-config-and-configobject)
- [ConfigBeanFactory](#configbeanfactory)
- [Using HOCON, the JSON Superset](#using-hocon-the-json-superset)
- [Features of HOCON](#features-of-hocon)
- [Examples of HOCON](#examples-of-hocon)
- [Uses of Substitutions](#uses-of-substitutions)
- [Factor out common values](#factor-out-common-values)
- [Inheritance](#inheritance)
- [Optional system or env variable overrides](#optional-system-or-env-variable-overrides)
- [Concatenation](#concatenation)
- [`reference.conf` can't refer to `application.conf`](#referenceconf-cant-refer-to-applicationconf)
- [Miscellaneous Notes](#miscellaneous-notes)
- [Debugging Your Configuration](#debugging-your-configuration)
- [Supports Java 8 and Later](#supports-java-8-and-later)
- [Rationale for Supported File Formats](#rationale-for-supported-file-formats)
- [Other APIs (Wrappers, Ports and Utilities)](#other-apis-wrappers-ports-and-utilities)
- [Guice integration](#guice-integration)
- [Java (yep!) wrappers for the Java library](#java-yep-wrappers-for-the-java-library)
- [Scala wrappers for the Java library](#scala-wrappers-for-the-java-library)
- [Clojure wrappers for the Java library](#clojure-wrappers-for-the-java-library)
- [Scala port](#scala-port)
- [Ruby port](#ruby-port)
- [Puppet module](#puppet-module)
- [Python port](#python-port)
- [C++ port](#c-port)
- [Linting tool](#linting-tool)
## Essential Information
### License
The license is Apache 2.0, see LICENSE-2.0.txt.
### Binary Releases
Version 1.2.1 and earlier were built for Java 6, while newer
versions (1.3.0 and above) will be built for Java 8.
You can find published releases on Maven Central.
com.typesafeconfig1.3.0
sbt dependency:
libraryDependencies += "com.typesafe" % "config" % "1.3.0"
Link for direct download if you don't use a dependency manager:
- http://central.maven.org/maven2/com/typesafe/config/
### Release Notes
Please see NEWS.md in this directory,
https://github.com/typesafehub/config/blob/master/NEWS.md
### API docs
- Online: http://typesafehub.github.com/config/latest/api/
- also published in jar form
- consider reading this README first for an intro
- for questions about the `.conf` file format, read
[HOCON.md](https://github.com/typesafehub/config/blob/master/HOCON.md)
in this directory
### Bugs and Patches
Report bugs to the GitHub issue tracker. Send patches as pull
requests on GitHub.
Before we can accept pull requests, you will need to agree to the
Typesafe Contributor License Agreement online, using your GitHub
account - it takes 30 seconds. You can do this at
http://www.typesafe.com/contribute/cla
Please see
[CONTRIBUTING](https://github.com/typesafehub/config/blob/master/CONTRIBUTING.md)
for more including how to make a release.
### Build
The build uses sbt and the tests are written in Scala; however,
the library itself is plain Java and the published jar has no
Scala dependency.
## Using the Library
### API Example
import com.typesafe.config.ConfigFactory
Config conf = ConfigFactory.load();
int bar1 = conf.getInt("foo.bar");
Config foo = conf.getConfig("foo");
int bar2 = foo.getInt("bar");
### Longer Examples
See the examples in the `examples/` [directory](https://github.com/typesafehub/config/tree/master/examples).
You can run these from the sbt console with the commands `project
config-simple-app-java` and then `run`.
In brief, as shown in the examples:
- libraries should use a `Config` instance provided by the app,
if any, and use `ConfigFactory.load()` if no special `Config`
is provided. Libraries should put their defaults in a
`reference.conf` on the classpath.
- apps can create a `Config` however they want
(`ConfigFactory.load()` is easiest and least-surprising), then
provide it to their libraries. A `Config` can be created with
the parser methods in `ConfigFactory` or built up from any file
format or data source you like with the methods in
`ConfigValueFactory`.
### Immutability
Objects are immutable, so methods on `Config` which transform the
configuration return a new `Config`. Other types such as
`ConfigParseOptions`, `ConfigResolveOptions`, `ConfigObject`,
etc. are also immutable. See the
[API docs](http://typesafehub.github.com/config/latest/api/) for
details of course.
### Schemas and Validation
There isn't a schema language or anything like that. However, two
suggested tools are:
- use the
[checkValid() method](http://typesafehub.github.com/config/latest/api/com/typesafe/config/Config.html#checkValid%28com.typesafe.config.Config,%20java.lang.String...%29)
- access your config through a Settings class with a field for
each setting, and instantiate it on startup (immediately
throwing an exception if any settings are missing)
In Scala, a Settings class might look like:
class Settings(config: Config) {
// validate vs. reference.conf
config.checkValid(ConfigFactory.defaultReference(), "simple-lib")
// non-lazy fields, we want all exceptions at construct time
val foo = config.getString("simple-lib.foo")
val bar = config.getInt("simple-lib.bar")
}
See the examples/ directory for a full compilable program using
this pattern.
### Standard behavior
The convenience method `ConfigFactory.load()` loads the following
(first-listed are higher priority):
- 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`.
(Applications _can_ provide a `reference.conf` also if they want,
but you may not find it necessary to separate it from
`application.conf`.)
Libraries and frameworks should default to `ConfigFactory.load()`
if the application does not provide a custom `Config` object. This
way, libraries will see configuration from `application.conf` and
users can configure the whole app, with its libraries, in a single
`application.conf` file.
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. The
library examples in `examples/` show how to accept a custom config
while defaulting to `ConfigFactory.load()`.
For applications using `application.{conf,json,properties}`,
system properties can be used to force a different config source:
- `config.resource` specifies a resource name - not a
basename, i.e. `application.conf` not `application`
- `config.file` specifies a filesystem path, again
it should include the extension, not be a basename
- `config.url` specifies a URL
These system properties specify a _replacement_ for
`application.{conf,json,properties}`, not an addition. They only
affect apps using the default `ConfigFactory.load()`
configuration. In the replacement config file, you can use
`include "application"` to include the original default config
file; after the include statement you could go on to override
certain settings.
If you set `config.resource`, `config.file`, or `config.url`
on-the-fly from inside your program (for example with
`System.setProperty()`), be aware that `ConfigFactory` has some
internal caches and may not see new values for system
properties. Use `ConfigFactory.invalidateCaches()` to force-reload
system properties.
#### Note about resolving substitutions in `reference.conf` and `application.conf`
The substitution syntax `${foo.bar}` will be resolved
twice. First, all the `reference.conf` files are merged and then
the result gets resolved. Second, all the `application.conf` are
layered over the `reference.conf` and the result of that gets
resolved again.
The implication of this is that the `reference.conf` stack has to
be self-contained; you can't leave an undefined value `${foo.bar}`
to be provided by `application.conf`, or refer to `${foo.bar}` in
a way that you want to allow `application.conf` to
override. However, `application.conf` can refer to a `${foo.bar}`
in `reference.conf`.
This can be frustrating at times, but possible workarounds
include:
* putting an `application.conf` in a library jar, alongside the
`reference.conf`, with values intended for later resolution.
* putting some logic in code instead of building up values in the
config itself.
### Merging config trees
Any two Config objects can be merged with an associative operation
called `withFallback`, like `merged = firstConfig.withFallback(secondConfig)`.
The `withFallback` operation is used inside the library to merge
duplicate keys in the same file and to merge multiple files.
`ConfigFactory.load()` uses it to stack system properties over
`application.conf` over `reference.conf`.
You can also use `withFallback` to merge in some hardcoded values,
or to "lift" a subtree up to the root of the configuration; say
you have something like:
foo=42
dev.foo=57
prod.foo=10
Then you could code something like:
Config devConfig = originalConfig
.getConfig("dev")
.withFallback(originalConfig)
There are lots of ways to use `withFallback`.
### How to handle defaults
Many other configuration APIs allow you to provide a default to
the getter methods, like this:
boolean getBoolean(String path, boolean fallback)
Here, if the path has no setting, the fallback would be
returned. An API could also return `null` for unset values, so you
would check for `null`:
// returns null on unset, check for null and fall back
Boolean getBoolean(String path)
The methods on the `Config` interface do NOT do this, for two
major reasons:
1. If you use a config setting in two places, the default
fallback value gets cut-and-pasted and typically out of
sync. This can result in Very Evil Bugs.
2. If the getter returns `null` (or `None`, in Scala) then every
time you get a setting you have to write handling code for
`null`/`None` and that code will almost always just throw an
exception. Perhaps more commonly, people forget to check for
`null` at all, so missing settings result in
`NullPointerException`.
For most situations, failure to have a setting is simply a bug to fix
(in either code or the deployment environment). Therefore, if a
setting is unset, by default the getters on the `Config` interface
throw an exception.
If you want to allow a setting to be missing from
`application.conf` in a particular case, then here are some
options:
1. Set it in a `reference.conf` included in your library or
application jar, so there's a default value.
2. Use the `Config.hasPath()` method to check in advance whether
the path exists (rather than checking for `null`/`None` after as
you might in other APIs).
3. Catch and handle `ConfigException.Missing`. NOTE: using an
exception for control flow like this is much slower than using
`Config.hasPath()`; the JVM has to do a lot of work to throw
an exception.
4. In your initialization code, generate a `Config` with your
defaults in it (using something like `ConfigFactory.parseMap()`)
then fold that default config into your loaded config using
`withFallback()`, and use the combined config in your
program. "Inlining" your reference config in the code like this
is probably less convenient than using a `reference.conf` file,
but there may be reasons to do it.
5. Use `Config.root()` to get the `ConfigObject` for the
`Config`; `ConfigObject` implements `java.util.Map` and
the `get()` method on `Map` returns null for missing keys. See
the API docs for more detail on `Config` vs. `ConfigObject`.
6. Set the setting to `null` in `reference.conf`, then use
`Config.getIsNull` and `Config.hasPathOrNull` to handle `null`
in a special way while still throwing an exception if the setting
is entirely absent.
The *recommended* path (for most cases, in most apps) is that you
require all settings to be present in either `reference.conf` or
`application.conf` and allow `ConfigException.Missing` to be
thrown if they are not. That's the design intent of the `Config`
API design.
Consider the "Settings class" pattern with `checkValid()` to
verify that you have all settings when you initialize the
app. See the [Schemas and Validation](#schemas-and-validation)
section of this README for more details on this pattern.
**If you do need a setting to be optional**: checking `hasPath()` in
advance should be the same amount of code (in Java) as checking
for `null` afterward, without the risk of `NullPointerException`
when you forget. In Scala, you could write an enrichment class
like this to use the idiomatic `Option` syntax:
```scala
implicit class RichConfig(val underlying: Config) extends AnyVal {
def getOptionalBoolean(path: String): Option[Boolean] = if (underlying.hasPath(path)) {
Some(underlying.getBoolean(path))
} else {
None
}
}
```
Since this library is a Java library it doesn't come with that out
of the box, of course.
It is understood that sometimes defaults in code make sense. For
example, if your configuration lets users invent new sections, you
may not have all paths up front and may be unable to set up
defaults in `reference.conf` for dynamic paths. The design intent
of `Config` isn't to *prohibit* inline defaults, but simply to
recognize that it seems to be the 10% case (rather than the 90%
case). Even in cases where dynamic defaults are needed, you may
find that using `withFallback()` to build a complete
nothing-missing `Config` in one central place in your code keeps
things tidy.
Whatever you do, please remember not to cut-and-paste default
values into multiple places in your code. You have been warned!
:-)
### Understanding `Config` and `ConfigObject`
To read and modify configuration, you'll use the
[Config](http://typesafehub.github.io/config/latest/api/com/typesafe/config/Config.html)
interface. A `Config` looks at a JSON-equivalent data structure as
a one-level map from paths to values. So if your JSON looks like
this:
```
"foo" : {
"bar" : 42
"baz" : 43
}
```
Using the `Config` interface, you could write
`conf.getInt("foo.bar")`. The `foo.bar` string is called a _path
expression_
([HOCON.md](https://github.com/typesafehub/config/blob/master/HOCON.md)
has the syntax details for these expressions). Iterating over this
`Config`, you would get two entries; `"foo.bar" : 42` and
`"foo.baz" : 43`. When iterating a `Config` you will not find
nested `Config` (because everything gets flattened into one
level).
When looking at a JSON tree as a `Config`, `null` values are
treated as if they were missing. Iterating over a `Config` will
skip `null` values.
You can also look at a `Config` in the way most JSON APIs would,
through the
[ConfigObject](http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigObject.html)
interface. This interface represents an object node in the JSON
tree. `ConfigObject` instances come in multi-level trees, and the
keys do not have any syntax (they are just strings, not path
expressions). Iterating over the above example as a
`ConfigObject`, you would get one entry `"foo" : { "bar" : 42,
"baz" : 43 }`, where the value at `"foo"` is another nested
`ConfigObject`.
In `ConfigObject`, `null` values are visible (distinct from
missing values), just as they are in JSON.
`ConfigObject` is a subtype of [ConfigValue](http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigValue.html), where the other
subtypes are the other JSON types (list, string, number, boolean, null).
`Config` and `ConfigObject` are two ways to look at the same
internal data structure, and you can convert between them for free
using
[Config.root()](http://typesafehub.github.io/config/latest/api/com/typesafe/config/Config.html#root%28%29)
and
[ConfigObject.toConfig()](http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigObject.html#toConfig%28%29).
### ConfigBeanFactory
As of version 1.3.0, if you have a Java object that follows
JavaBean conventions (zero-args constructor, getters and setters),
you can automatically initialize it from a `Config`.
Use
`ConfigBeanFactory.create(config.getConfig("subtree-that-matches-bean"),
MyBean.class)` to do this.
Creating a bean from a `Config` automatically validates that the
config matches the bean's implied schema. Bean fields can be
primitive types, typed lists such as `List`,
`java.time.Duration`, `ConfigMemorySize`, or even a raw `Config`,
`ConfigObject`, or `ConfigValue` (if you'd like to deal with a
particular value manually).
## Using HOCON, the JSON Superset
The JSON superset is called "Human-Optimized Config Object
Notation" or HOCON, and files use the suffix `.conf`. See
[HOCON.md](https://github.com/typesafehub/config/blob/master/HOCON.md)
in this directory for more detail.
After processing a `.conf` file, the result is always just a JSON
tree that you could have written (less conveniently) in JSON.
### Features of HOCON
- Comments, with `#` or `//`
- Allow omitting the `{}` around a root object
- Allow `=` as a synonym for `:`
- Allow omitting the `=` or `:` before a `{` so
`foo { a : 42 }`
- Allow omitting commas as long as there's a newline
- Allow trailing commas after last element in objects and arrays
- Allow unquoted strings for keys and values
- Unquoted keys can use dot-notation for nested objects,
`foo.bar=42` means `foo { bar : 42 }`
- Duplicate keys are allowed; later values override earlier,
except for object-valued keys where the two objects are merged
recursively
- `include` feature merges root object in another file into
current object, so `foo { include "bar.json" }` merges keys in
`bar.json` into the object `foo`
- include with no file extension includes any of `.conf`,
`.json`, `.properties`
- you can include files, URLs, or classpath resources; use
`include url("http://example.com")` or `file()` or
`classpath()` syntax to force the type, or use just `include
"whatever"` to have the library do what you probably mean
(Note: `url()`/`file()`/`classpath()` syntax is not supported
in Play/Akka 2.0, only in later releases.)
- substitutions `foo : ${a.b}` sets key `foo` to the same value
as the `b` field in the `a` object
- substitutions concatenate into unquoted strings, `foo : the
quick ${colors.fox} jumped`
- substitutions fall back to environment variables if they don't
resolve in the config itself, so `${HOME}` would work as you
expect. Also, most configs have system properties merged in so
you could use `${user.home}`.
- substitutions normally cause an error if unresolved, but
there is a syntax `${?a.b}` to permit them to be missing.
- `+=` syntax to append elements to arrays, `path += "/bin"`
- multi-line strings with triple quotes as in Python or Scala
### Examples of HOCON
All of these are valid HOCON.
Start with valid JSON:
{
"foo" : {
"bar" : 10,
"baz" : 12
}
}
Drop root braces:
"foo" : {
"bar" : 10,
"baz" : 12
}
Drop quotes:
foo : {
bar : 10,
baz : 12
}
Use `=` and omit it before `{`:
foo {
bar = 10,
baz = 12
}
Remove commas:
foo {
bar = 10
baz = 12
}
Use dotted notation for unquoted keys:
foo.bar=10
foo.baz=12
Put the dotted-notation fields on a single line:
foo.bar=10, foo.baz=12
The syntax is well-defined (including handling of whitespace and
escaping). But it handles many reasonable ways you might want to
format the file.
Note that while you can write HOCON that looks a lot like a Java
properties file (and many properties files will parse as HOCON),
the details of escaping, whitespace handling, comments, and so
forth are more like JSON. The spec (see HOCON.md in this
directory) has some more detailed notes on this topic.
### Uses of Substitutions
The `${foo.bar}` substitution feature lets you avoid cut-and-paste
in some nice ways.
#### Factor out common values
This is the obvious use,
standard-timeout = 10ms
foo.timeout = ${standard-timeout}
bar.timeout = ${standard-timeout}
#### Inheritance
If you duplicate a field with an object value, then the objects
are merged with last-one-wins. So:
foo = { a : 42, c : 5 }
foo = { b : 43, c : 6 }
means the same as:
foo = { a : 42, b : 43, c : 6 }
You can take advantage of this for "inheritance":
data-center-generic = { cluster-size = 6 }
data-center-east = ${data-center-generic}
data-center-east = { name = "east" }
data-center-west = ${data-center-generic}
data-center-west = { name = "west", cluster-size = 8 }
Using `include` statements you could split this across multiple
files, too.
If you put two objects next to each other (close brace of the first
on the same line with open brace of the second), they are merged, so
a shorter way to write the above "inheritance" example would be:
data-center-generic = { cluster-size = 6 }
data-center-east = ${data-center-generic} { name = "east" }
data-center-west = ${data-center-generic} { name = "west", cluster-size = 8 }
#### Optional system or env variable overrides
In default uses of the library, exact-match system properties
already override the corresponding config properties. However,
you can add your own overrides, or allow environment variables to
override, using the `${?foo}` substitution syntax.
basedir = "/whatever/whatever"
basedir = ${?FORCED_BASEDIR}
Here, the override field `basedir = ${?FORCED_BASEDIR}` simply
vanishes if there's no value for `FORCED_BASEDIR`, but if you set
an environment variable `FORCED_BASEDIR` for example, it would be
used.
A natural extension of this idea is to support several different
environment variable names or system property names, if you aren't
sure which one will exist in the target environment.
Object fields and array elements with a `${?foo}` substitution
value just disappear if the substitution is not found:
// this array could have one or two elements
path = [ "a", ${?OPTIONAL_A} ]
### Concatenation
Values _on the same line_ are concatenated (for strings and
arrays) or merged (for objects).
This is why unquoted strings work, here the number `42` and the
string `foo` are concatenated into a string `42 foo`:
key : 42 foo
When concatenating values into a string, leading and trailing
whitespace is stripped but whitespace between values is kept.
Unquoted strings also support substitutions of course:
tasks-url : ${base-url}/tasks
A concatenation can refer to earlier values of the same field:
path : "/bin"
path : ${path}":/usr/bin"
Arrays can be concatenated as well:
path : [ "/bin" ]
path : ${path} [ "/usr/bin" ]
There is a shorthand for appending to arrays:
// equivalent to: path = ${?path} [ "/usr/bin" ]
path += "/usr/bin"
To prepend or insert into an array, there is no shorthand.
When objects are "concatenated," they are merged, so object
concatenation is just a shorthand for defining the same object
twice. The long way (mentioned earlier) is:
data-center-generic = { cluster-size = 6 }
data-center-east = ${data-center-generic}
data-center-east = { name = "east" }
The concatenation-style shortcut is:
data-center-generic = { cluster-size = 6 }
data-center-east = ${data-center-generic} { name = "east" }
When concatenating objects and arrays, newlines are allowed
_inside_ each object or array, but not between them.
Non-newline whitespace is never a field or element separator. So
`[ 1 2 3 4 ]` is an array with one unquoted string element
`"1 2 3 4"`. To get an array of four numbers you need either commas or
newlines separating the numbers.
See the spec for full details on concatenation.
Note: Play/Akka 2.0 have an earlier version that supports string
concatenation, but not object/array concatenation. `+=` does not
work in Play/Akka 2.0 either. Post-2.0 versions support these
features.
### `reference.conf` can't refer to `application.conf`
Please see this
earlier section; all `reference.conf` have substitutions
resolved first, without `application.conf` in the stack, so the
reference stack has to be self-contained.
## Miscellaneous Notes
### Debugging Your Configuration
If you have trouble with your configuration, some useful tips.
- Set the Java system property `-Dconfig.trace=loads` to get
output on stderr describing each file that is loaded.
Note: this feature is not included in the older version in
Play/Akka 2.0.
- Use `myConfig.root().render()` to get a `Config` printed out as a
string with comments showing where each value came from.
### Supports Java 8 and Later
Currently the library is maintained against Java 8, but
version 1.2.1 and earlier will work with Java 6.
Please use 1.2.1 if you need Java 6 support, though some people
have expressed interest in a branch off of 1.3.0 supporting
Java 7. If you want to work on that branch you might bring it up
on [chat](https://gitter.im/typesafehub/config). We can release a
jar for Java 7 if someone(s) steps up to maintain the branch. The
master branch does not use Java 8 "gratuitously" but some APIs
that use Java 8 types will need to be removed.
### Rationale for Supported File Formats
(For the curious.)
The three file formats each have advantages.
- Java `.properties`:
- Java standard, built in to JVM
- Supported by many tools such as IDEs
- JSON:
- easy to generate programmatically
- well-defined and standard
- bad for human maintenance, with no way to write comments,
and no mechanisms to avoid duplication of similar config
sections
- HOCON/`.conf`:
- nice for humans to read, type, and maintain, with more
lenient syntax
- built-in tools to avoid cut-and-paste
- ways to refer to the system environment, such as system
properties and environment variables
The idea would be to use JSON if you're writing a script to spit
out config, and use HOCON if you're maintaining config by hand.
If you're doing both, then mix the two.
Two alternatives to HOCON syntax could be:
- YAML is also a JSON superset and has a mechanism for adding
custom types, so the include statements in HOCON could become
a custom type tag like `!include`, and substitutions in HOCON
could become a custom tag such as `!subst`, for example. The
result is somewhat clunky to write, but would have the same
in-memory representation as the HOCON approach.
- Put a syntax inside JSON strings, so you might write something
like `"$include" : "filename"` or allow `"foo" : "${bar}"`.
This is a way to tunnel new syntax through a JSON parser, but
other than the implementation benefit (using a standard JSON
parser), it doesn't really work. It's a bad syntax for human
maintenance, and it's not valid JSON anymore because properly
interpreting it requires treating some valid JSON strings as
something other than plain strings. A better approach is to
allow mixing true JSON files into the config but also support
a nicer format.
### Other APIs (Wrappers, Ports and Utilities)
This may not be comprehensive - if you'd like to add mention of
your wrapper, just send a pull request for this README. We would
love to know what you're doing with this library or with the HOCON
format.
#### Guice integration
* Typesafe Config Guice https://github.com/racc/typesafeconfig-guice
#### Java (yep!) wrappers for the Java library
* tscfg https://github.com/carueda/tscfg
#### Scala wrappers for the Java library
* Ficus https://github.com/ceedubs/ficus
* configz https://github.com/arosien/configz
* configs https://github.com/kxbmap/configs
* config-annotation https://github.com/wacai/config-annotation
* PureConfig https://github.com/melrief/pureconfig
* Simple Scala Config https://github.com/ElderResearch/ssc
* konfig https://github.com/vpon/konfig
#### Clojure wrappers for the Java library
* beamly-core.config https://github.com/beamly/beamly-core.config
#### Scala port
* SHocon https://github.com/unicredit/shocon (work with both Scala and Scala.Js)
#### Ruby port
* https://github.com/puppetlabs/ruby-hocon
#### Puppet module
* Manage your HOCON configuration files with Puppet!: https://forge.puppetlabs.com/puppetlabs/hocon
#### Python port
* pyhocon https://github.com/chimpler/pyhocon
#### C++ port
* https://github.com/puppetlabs/cpp-hocon
#### Linting tool
* A web based linting tool http://www.hoconlint.com/
config-1.3.1/appveyor.yml 0000664 0000000 0000000 00000000510 12771472746 0015366 0 ustar 00root root 0000000 0000000 version: '{build}'
os: Windows Server 2012
install:
- cmd: choco install sbt -ia "INSTALLDIR=""C:\sbt"""
- cmd: SET PATH=C:\sbt\bin;%JAVA_HOME%\bin;%PATH%
- cmd: SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g
build_script:
- sbt clean compile
test_script:
- sbt test
- sbt doc
cache:
- C:\sbt\
- C:\Users\appveyor\.ivy2
config-1.3.1/build.sbt 0000664 0000000 0000000 00000001361 12771472746 0014614 0 ustar 00root root 0000000 0000000 // to release, bump major/minor/micro as appropriate,
// update NEWS, update version in README.md, tag, then
// publishSigned.
// Release tags should follow: http://semver.org/
enablePlugins(GitVersioning)
git.baseVersion := "1.3.0"
organization in GlobalScope := "com.typesafe"
scalacOptions in GlobalScope in Compile := Seq("-unchecked", "-deprecation", "-feature")
scalacOptions in GlobalScope in Test := Seq("-unchecked", "-deprecation", "-feature")
scalaVersion in ThisBuild := "2.10.4"
useGpg := true
aggregate in PgpKeys.publishSigned := false
PgpKeys.publishSigned := (PgpKeys.publishSigned in configLib).value
aggregate in PgpKeys.publishLocalSigned := false
PgpKeys.publishLocalSigned := (PgpKeys.publishLocalSigned in configLib).value
config-1.3.1/config/ 0000775 0000000 0000000 00000000000 12771472746 0014247 5 ustar 00root root 0000000 0000000 config-1.3.1/config/.gitignore 0000664 0000000 0000000 00000000005 12771472746 0016232 0 ustar 00root root 0000000 0000000 /bin
config-1.3.1/config/build.sbt 0000664 0000000 0000000 00000006125 12771472746 0016064 0 ustar 00root root 0000000 0000000 import de.johoop.findbugs4sbt.FindBugs._
import de.johoop.findbugs4sbt.{ Effort, ReportType }
import de.johoop.jacoco4sbt.JacocoPlugin.jacoco
import com.typesafe.sbt.SbtScalariform
import com.typesafe.sbt.SbtScalariform.ScalariformKeys
import scalariform.formatter.preferences._
SbtScalariform.scalariformSettings
val formatPrefs = FormattingPreferences()
.setPreference(IndentSpaces, 4)
ScalariformKeys.preferences in Compile := formatPrefs
ScalariformKeys.preferences in Test := formatPrefs
fork in test := true
fork in run := true
fork in run in Test := true
autoScalaLibrary := false
crossPaths := false
libraryDependencies += "net.liftweb" %% "lift-json" % "2.5" % "test"
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
externalResolvers += "Scala Tools Snapshots" at "http://scala-tools.org/repo-snapshots/"
checkstyleConfigLocation := CheckstyleConfigLocation.File((baseDirectory.value / "checkstyle-config.xml").toString)
checkstyle in Compile := {
val log = streams.value.log
(checkstyle in Compile).value
val resultFile = (checkstyleOutputFile in Compile).value
val results = scala.xml.XML.loadFile(resultFile)
val errorFiles = results \\ "checkstyle" \\ "file"
def errorFromXml(node: scala.xml.NodeSeq): (String, String, String) = {
val line: String = (node \ "@line" text)
val msg: String = (node \ "@message" text)
val source: String = (node \ "@source" text)
(line, msg, source)
}
def errorsFromXml(fileNode: scala.xml.NodeSeq): Seq[(String, String, String, String)] = {
val name: String = (fileNode \ "@name" text)
val errors = (fileNode \\ "error") map { e => errorFromXml(e) }
errors map { case (line, error, source) => (name, line, error, source) }
}
val errors = errorFiles flatMap { f => errorsFromXml(f) }
if (errors.nonEmpty) {
for (e <- errors) {
log.error(s"${e._1}:${e._2}: ${e._3} (from ${e._4})")
}
throw new RuntimeException(s"Checkstyle failed with ${errors.size} errors")
}
log.info("No errors from checkstyle")
}
// add checkstyle as a dependency of doc
doc in Compile <<= (doc in Compile).dependsOn(checkstyle in Compile)
findbugsSettings
findbugsReportType := Some(ReportType.Html)
findbugsReportPath := Some(crossTarget.value / "findbugs.html")
findbugsEffort := Effort.Maximum
findbugsMaxMemory := 2000
jacoco.settings
javacOptions in (Compile, compile) ++= Seq("-source", "1.6", "-target", "1.8",
"-g", "-Xlint:unchecked")
// because we test some global state such as singleton caches,
// we have to run tests in serial.
parallelExecution in Test := false
javacOptions in (Compile, doc) ++= Seq("-group", s"Public API (version ${version.value})", "com.typesafe.config:com.typesafe.config.parser",
"-group", "Internal Implementation - Not ABI Stable", "com.typesafe.config.impl")
javadocSourceBaseUrl := {
for (gitHead <- com.typesafe.sbt.SbtGit.GitKeys.gitHeadCommit.value)
yield s"https://github.com/typesafehub/config/blob/$gitHead/config/src/main/java"
}
javaVersionPrefix in javaVersionCheck := Some("1.8")
config-1.3.1/config/checkstyle-config.xml 0000664 0000000 0000000 00000001213 12771472746 0020367 0 ustar 00root root 0000000 0000000
config-1.3.1/config/checkstyle-suppressions.xml 0000664 0000000 0000000 00000001155 12771472746 0021704 0 ustar 00root root 0000000 0000000
config-1.3.1/config/src/ 0000775 0000000 0000000 00000000000 12771472746 0015036 5 ustar 00root root 0000000 0000000 config-1.3.1/config/src/main/ 0000775 0000000 0000000 00000000000 12771472746 0015762 5 ustar 00root root 0000000 0000000 config-1.3.1/config/src/main/java/ 0000775 0000000 0000000 00000000000 12771472746 0016703 5 ustar 00root root 0000000 0000000 config-1.3.1/config/src/main/java/com/ 0000775 0000000 0000000 00000000000 12771472746 0017461 5 ustar 00root root 0000000 0000000 config-1.3.1/config/src/main/java/com/typesafe/ 0000775 0000000 0000000 00000000000 12771472746 0021301 5 ustar 00root root 0000000 0000000 config-1.3.1/config/src/main/java/com/typesafe/config/ 0000775 0000000 0000000 00000000000 12771472746 0022546 5 ustar 00root root 0000000 0000000 config-1.3.1/config/src/main/java/com/typesafe/config/Config.java 0000664 0000000 0000000 00000125423 12771472746 0024625 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* An immutable map from config paths to config values. Paths are dot-separated
* expressions such as foo.bar.baz. Values are as in JSON
* (booleans, strings, numbers, lists, or objects), represented by
* {@link ConfigValue} instances. Values accessed through the
* Config interface are never null.
*
*
* {@code Config} is an immutable object and thus safe to use from multiple
* threads. There's never a need for "defensive copies."
*
*
* Fundamental operations on a {@code Config} include getting configuration
* values, resolving substitutions with {@link Config#resolve()}, and
* merging configs using {@link Config#withFallback(ConfigMergeable)}.
*
*
* All operations return a new immutable {@code Config} rather than modifying
* the original instance.
*
*
* Examples
*
*
* You can find an example app and library on
* GitHub. Also be sure to read the package overview which
* describes the big picture as shown in those examples.
*
*
* Paths, keys, and Config vs. ConfigObject
*
*
* Config is a view onto a tree of {@link ConfigObject}; the
* corresponding object tree can be found through {@link Config#root()}.
* ConfigObject is a map from config keys, rather than
* paths, to config values. Think of ConfigObject as a JSON object
* and Config as a configuration API.
*
*
* The API tries to consistently use the terms "key" and "path." A key is a key
* in a JSON object; it's just a string that's the key in a map. A "path" is a
* parseable expression with a syntax and it refers to a series of keys. Path
* expressions are described in the spec for
* Human-Optimized Config Object Notation. In brief, a path is
* period-separated so "a.b.c" looks for key c in object b in object a in the
* root object. Sometimes double quotes are needed around special characters in
* path expressions.
*
*
* The API for a {@code Config} is in terms of path expressions, while the API
* for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config}
* is a one-level map from paths to values, while a
* {@code ConfigObject} is a tree of nested maps from keys to values.
*
*
* Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert
* between path expressions and individual path elements (keys).
*
*
* Another difference between {@code Config} and {@code ConfigObject} is that
* conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType()
* valueType()} of {@link ConfigValueType#NULL NULL} exist in a
* {@code ConfigObject}, while a {@code Config} treats null values as if they
* were missing. (With the exception of two methods: {@link Config#hasPathOrNull}
* and {@link Config#getIsNull} let you detect null values.)
*
*
* Getting configuration values
*
*
* The "getters" on a {@code Config} all work in the same way. They never return
* null, nor do they return a {@code ConfigValue} with
* {@link ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL
* NULL}. Instead, they throw {@link ConfigException.Missing} if the value is
* completely absent or set to null. If the value is set to null, a subtype of
* {@code ConfigException.Missing} called {@link ConfigException.Null} will be
* thrown. {@link ConfigException.WrongType} will be thrown anytime you ask for
* a type and the value has an incompatible type. Reasonable type conversions
* are performed for you though.
*
*
* Iteration
*
*
* If you want to iterate over the contents of a {@code Config}, you can get its
* {@code ConfigObject} with {@link #root()}, and then iterate over the
* {@code ConfigObject} (which implements java.util.Map). Or, you
* can use {@link #entrySet()} which recurses the object tree for you and builds
* up a Set of all path-value pairs where the value is not null.
*
*
* Resolving substitutions
*
*
* Substitutions are the ${foo.bar} syntax in config
* files, described in the specification. Resolving substitutions replaces these references with real
* values.
*
*
* Before using a {@code Config} it's necessary to call {@link Config#resolve()}
* to handle substitutions (though {@link ConfigFactory#load()} and similar
* methods will do the resolve for you already).
*
*
* Merging
*
*
* The full Config for your application can be constructed using
* the associative operation {@link Config#withFallback(ConfigMergeable)}. If
* you use {@link ConfigFactory#load()} (recommended), it merges system
* properties over the top of application.conf over the top of
* reference.conf, using withFallback. You can add in
* additional sources of configuration in the same way (usually, custom layers
* should go either just above or just below application.conf,
* keeping reference.conf at the bottom and system properties at
* the top).
*
*
* Serialization
*
*
* Convert a Config to a JSON or HOCON string by calling
* {@link ConfigObject#render()} on the root object,
* myConfig.root().render(). There's also a variant
* {@link ConfigObject#render(ConfigRenderOptions)} which allows you to control
* the format of the rendered string. (See {@link ConfigRenderOptions}.) Note
* that Config does not remember the formatting of the original
* file, so if you load, modify, and re-save a config file, it will be
* substantially reformatted.
*
*
* As an alternative to {@link ConfigObject#render()}, the
* toString() method produces a debug-output-oriented
* representation (which is not valid JSON).
*
*
* Java serialization is supported as well for Config and all
* subtypes of ConfigValue.
*
*
* This is an interface but don't implement it yourself
*
*
* Do not implement {@code Config}; it should only be implemented by
* the config library. Arbitrary implementations will not work because the
* library internals assume a specific concrete implementation. Also, this
* interface is likely to grow new methods over time, so third-party
* implementations will break.
*/
public interface Config extends ConfigMergeable {
/**
* Gets the {@code Config} as a tree of {@link ConfigObject}. This is a
* constant-time operation (it is not proportional to the number of values
* in the {@code Config}).
*
* @return the root object in the configuration
*/
ConfigObject root();
/**
* Gets the origin of the {@code Config}, which may be a file, or a file
* with a line number, or just a descriptive phrase.
*
* @return the origin of the {@code Config} for use in error messages
*/
ConfigOrigin origin();
@Override
Config withFallback(ConfigMergeable other);
/**
* Returns a replacement config with all substitutions (the
* ${foo.bar} syntax, see the
* spec) resolved. Substitutions are looked up using this
* Config as the root object, that is, a substitution
* ${foo.bar} will be replaced with the result of
* getValue("foo.bar").
*
*
* This method uses {@link ConfigResolveOptions#defaults()}, there is
* another variant {@link Config#resolve(ConfigResolveOptions)} which lets
* you specify non-default options.
*
*
* A given {@link Config} must be resolved before using it to retrieve
* config values, but ideally should be resolved one time for your entire
* stack of fallbacks (see {@link Config#withFallback}). Otherwise, some
* substitutions that could have resolved with all fallbacks available may
* not resolve, which will be potentially confusing for your application's
* users.
*
*
* resolve() should be invoked on root config objects, rather
* than on a subtree (a subtree is the result of something like
* config.getConfig("foo")). The problem with
* resolve() on a subtree is that substitutions are relative to
* the root of the config and the subtree will have no way to get values
* from the root. For example, if you did
* config.getConfig("foo").resolve() on the below config file,
* it would not work:
*
*
* Many methods on {@link ConfigFactory} such as
* {@link ConfigFactory#load()} automatically resolve the loaded
* Config on the loaded stack of config files.
*
*
* Resolving an already-resolved config is a harmless no-op, but again, it
* is best to resolve an entire stack of fallbacks (such as all your config
* files combined) rather than resolving each one individually.
*
* @return an immutable object with substitutions resolved
* @throws ConfigException.UnresolvedSubstitution
* if any substitutions refer to nonexistent paths
* @throws ConfigException
* some other config exception if there are other problems
*/
Config resolve();
/**
* Like {@link Config#resolve()} but allows you to specify non-default
* options.
*
* @param options
* resolve options
* @return the resolved Config (may be only partially resolved if options are set to allow unresolved)
*/
Config resolve(ConfigResolveOptions options);
/**
* Checks whether the config is completely resolved. After a successful call
* to {@link Config#resolve()} it will be completely resolved, but after
* calling {@link Config#resolve(ConfigResolveOptions)} with
* allowUnresolved set in the options, it may or may not be
* completely resolved. A newly-loaded config may or may not be completely
* resolved depending on whether there were substitutions present in the
* file.
*
* @return true if there are no unresolved substitutions remaining in this
* configuration.
* @since 1.2.0
*/
boolean isResolved();
/**
* Like {@link Config#resolve()} except that substitution values are looked
* up in the given source, rather than in this instance. This is a
* special-purpose method which doesn't make sense to use in most cases;
* it's only needed if you're constructing some sort of app-specific custom
* approach to configuration. The more usual approach if you have a source
* of substitution values would be to merge that source into your config
* stack using {@link Config#withFallback} and then resolve.
*
* Note that this method does NOT look in this instance for substitution
* values. If you want to do that, you could either merge this instance into
* your value source using {@link Config#withFallback}, or you could resolve
* multiple times with multiple sources (using
* {@link ConfigResolveOptions#setAllowUnresolved(boolean)} so the partial
* resolves don't fail).
*
* @param source
* configuration to pull values from
* @return an immutable object with substitutions resolved
* @throws ConfigException.UnresolvedSubstitution
* if any substitutions refer to paths which are not in the
* source
* @throws ConfigException
* some other config exception if there are other problems
* @since 1.2.0
*/
Config resolveWith(Config source);
/**
* Like {@link Config#resolveWith(Config)} but allows you to specify
* non-default options.
*
* @param source
* source configuration to pull values from
* @param options
* resolve options
* @return the resolved Config (may be only partially resolved
* if options are set to allow unresolved)
* @since 1.2.0
*/
Config resolveWith(Config source, ConfigResolveOptions options);
/**
* Validates this config against a reference config, throwing an exception
* if it is invalid. The purpose of this method is to "fail early" with a
* comprehensive list of problems; in general, anything this method can find
* would be detected later when trying to use the config, but it's often
* more user-friendly to fail right away when loading the config.
*
*
* Using this method is always optional, since you can "fail late" instead.
*
*
* You must restrict validation to paths you "own" (those whose meaning are
* defined by your code module). If you validate globally, you may trigger
* errors about paths that happen to be in the config but have nothing to do
* with your module. It's best to allow the modules owning those paths to
* validate them. Also, if every module validates only its own stuff, there
* isn't as much redundant work being done.
*
*
* If no paths are specified in checkValid()'s parameter list,
* validation is for the entire config.
*
*
* If you specify paths that are not in the reference config, those paths
* are ignored. (There's nothing to validate.)
*
*
* Here's what validation involves:
*
*
*
All paths found in the reference config must be present in this
* config or an exception will be thrown.
*
* Some changes in type from the reference config to this config will cause
* an exception to be thrown. Not all potential type problems are detected,
* in particular it's assumed that strings are compatible with everything
* except objects and lists. This is because string types are often "really"
* some other type (system properties always start out as strings, or a
* string like "5ms" could be used with {@link #getMilliseconds}). Also,
* it's allowed to set any type to null or override null with any type.
*
* Any unresolved substitutions in this config will cause a validation
* failure; both the reference config and this config should be resolved
* before validation. If the reference config is unresolved, it's a bug in
* the caller of this method.
*
*
*
* If you want to allow a certain setting to have a flexible type (or
* otherwise want validation to be looser for some settings), you could
* either remove the problematic setting from the reference config provided
* to this method, or you could intercept the validation exception and
* screen out certain problems. Of course, this will only work if all other
* callers of this method are careful to restrict validation to their own
* paths, as they should be.
*
*
* If validation fails, the thrown exception contains a list of all problems
* found. See {@link ConfigException.ValidationFailed#problems}. The
* exception's getMessage() will have all the problems
* concatenated into one huge string, as well.
*
*
* Again, checkValid() can't guess every domain-specific way a
* setting can be invalid, so some problems may arise later when attempting
* to use the config. checkValid() is limited to reporting
* generic, but common, problems such as missing settings and blatant type
* incompatibilities.
*
* @param reference
* a reference configuration
* @param restrictToPaths
* only validate values underneath these paths that your code
* module owns and understands
* @throws ConfigException.ValidationFailed
* if there are any validation issues
* @throws ConfigException.NotResolved
* if this config is not resolved
* @throws ConfigException.BugOrBroken
* if the reference config is unresolved or caller otherwise
* misuses the API
*/
void checkValid(Config reference, String... restrictToPaths);
/**
* Checks whether a value is present and non-null at the given path. This
* differs in two ways from {@code Map.containsKey()} as implemented by
* {@link ConfigObject}: it looks for a path expression, not a key; and it
* returns false for null values, while {@code containsKey()} returns true
* indicating that the object contains a null value for the key.
*
*
* If a path exists according to {@link #hasPath(String)}, then
* {@link #getValue(String)} will never throw an exception. However, the
* typed getters, such as {@link #getInt(String)}, will still throw if the
* value is not convertible to the requested type.
*
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* the path expression
* @return true if a non-null value is present at the path
* @throws ConfigException.BadPath
* if the path expression is invalid
*/
boolean hasPath(String path);
/**
* Checks whether a value is present at the given path, even
* if the value is null. Most of the getters on
* Config will throw if you try to get a null
* value, so if you plan to call {@link #getValue(String)},
* {@link #getInt(String)}, or another getter you may want to
* use plain {@link #hasPath(String)} rather than this method.
*
*
* To handle all three cases (unset, null, and a non-null value)
* the code might look like:
*
* if (config.hasPathOrNull(path)) {
* if (config.getIsNull(path)) {
* // handle null setting
* } else {
* // get and use non-null setting
* }
* } else {
* // handle entirely unset path
* }
*
*
*
However, the usual thing is to allow entirely unset
* paths to be a bug that throws an exception (because you set
* a default in your reference.conf), so in that
* case it's OK to call {@link #getIsNull(String)} without
* checking hasPathOrNull first.
*
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* the path expression
* @return true if a value is present at the path, even if the value is null
* @throws ConfigException.BadPath
* if the path expression is invalid
*/
boolean hasPathOrNull(String path);
/**
* Returns true if the {@code Config}'s root object contains no key-value
* pairs.
*
* @return true if the configuration is empty
*/
boolean isEmpty();
/**
* Returns the set of path-value pairs, excluding any null values, found by
* recursing {@link #root() the root object}. Note that this is very
* different from root().entrySet() which returns the set of
* immediate-child keys in the root object and includes null values.
*
* Entries contain path expressions meaning there may be quoting
* and escaping involved. Parse path expressions with
* {@link ConfigUtil#splitPath}.
*
* Because a Config is conceptually a single-level map from
* paths to values, there will not be any {@link ConfigObject} values in the
* entries (that is, all entries represent leaf nodes). Use
* {@link ConfigObject} rather than Config if you want a tree.
* (OK, this is a slight lie: Config entries may contain
* {@link ConfigList} and the lists may contain objects. But no objects are
* directly included as entry values.)
*
* @return set of paths with non-null values, built up by recursing the
* entire tree of {@link ConfigObject} and creating an entry for
* each leaf value.
*/
Set> entrySet();
/**
* Checks whether a value is set to null at the given path,
* but throws an exception if the value is entirely
* unset. This method will not throw if {@link
* #hasPathOrNull(String)} returned true for the same path, so
* to avoid any possible exception check
* hasPathOrNull() first. However, an exception
* for unset paths will usually be the right thing (because a
* reference.conf should exist that has the path
* set, the path should never be unset unless something is
* broken).
*
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* the path expression
* @return true if the value exists and is null, false if it
* exists and is not null
* @throws ConfigException.BadPath
* if the path expression is invalid
* @throws ConfigException.Missing
* if value is not set at all
*/
boolean getIsNull(String path);
/**
*
* @param path
* path expression
* @return the boolean value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to boolean
*/
boolean getBoolean(String path);
/**
* @param path
* path expression
* @return the numeric value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a number
*/
Number getNumber(String path);
/**
* Gets the integer at the given path. If the value at the
* path has a fractional (floating point) component, it
* will be discarded and only the integer part will be
* returned (it works like a "narrowing primitive conversion"
* in the Java language specification).
*
* @param path
* path expression
* @return the 32-bit integer value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to an int (for example it is out
* of range, or it's a boolean value)
*/
int getInt(String path);
/**
* Gets the long integer at the given path. If the value at
* the path has a fractional (floating point) component, it
* will be discarded and only the integer part will be
* returned (it works like a "narrowing primitive conversion"
* in the Java language specification).
*
* @param path
* path expression
* @return the 64-bit long value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a long
*/
long getLong(String path);
/**
* @param path
* path expression
* @return the floating-point value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a double
*/
double getDouble(String path);
/**
* @param path
* path expression
* @return the string value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a string
*/
String getString(String path);
/**
* @param enumClass
* an enum class
* @param
* a generic denoting a specific type of enum
* @param path
* path expression
* @return the {@code Enum} value at the requested path
* of the requested enum class
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to an Enum
*/
public > T getEnum(Class enumClass, String path);
/**
* @param path
* path expression
* @return the {@link ConfigObject} value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to an object
*/
ConfigObject getObject(String path);
/**
* @param path
* path expression
* @return the nested {@code Config} value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a Config
*/
Config getConfig(String path);
/**
* Gets the value at the path as an unwrapped Java boxed value (
* {@link java.lang.Boolean Boolean}, {@link java.lang.Integer Integer}, and
* so on - see {@link ConfigValue#unwrapped()}).
*
* @param path
* path expression
* @return the unwrapped value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
*/
Object getAnyRef(String path);
/**
* Gets the value at the given path, unless the value is a
* null value or missing, in which case it throws just like
* the other getters. Use {@code get()} on the {@link
* Config#root()} object (or other object in the tree) if you
* want an unprocessed value.
*
* @param path
* path expression
* @return the value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
*/
ConfigValue getValue(String path);
/**
* Gets a value as a size in bytes (parses special strings like "128M"). If
* the value is already a number, then it's left alone; if it's a string,
* it's parsed understanding unit suffixes such as "128K", as documented in
* the the
* spec.
*
* @param path
* path expression
* @return the value at the requested path, in bytes
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to Long or String
* @throws ConfigException.BadValue
* if value cannot be parsed as a size in bytes
*/
Long getBytes(String path);
/**
* Gets a value as an amount of memory (parses special strings like "128M"). If
* the value is already a number, then it's left alone; if it's a string,
* it's parsed understanding unit suffixes such as "128K", as documented in
* the the
* spec.
*
* @since 1.3.0
*
* @param path
* path expression
* @return the value at the requested path, in bytes
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to Long or String
* @throws ConfigException.BadValue
* if value cannot be parsed as a size in bytes
*/
ConfigMemorySize getMemorySize(String path);
/**
* Get value as a duration in milliseconds. If the value is already a
* number, then it's left alone; if it's a string, it's parsed understanding
* units suffixes like "10m" or "5ns" as documented in the the
* spec.
*
* @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)}
*
* @param path
* path expression
* @return the duration value at the requested path, in milliseconds
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to Long or String
* @throws ConfigException.BadValue
* if value cannot be parsed as a number of milliseconds
*/
@Deprecated Long getMilliseconds(String path);
/**
* Get value as a duration in nanoseconds. If the value is already a number
* it's taken as milliseconds and converted to nanoseconds. If it's a
* string, it's parsed understanding unit suffixes, as for
* {@link #getDuration(String, TimeUnit)}.
*
* @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)}
*
* @param path
* path expression
* @return the duration value at the requested path, in nanoseconds
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to Long or String
* @throws ConfigException.BadValue
* if value cannot be parsed as a number of nanoseconds
*/
@Deprecated Long getNanoseconds(String path);
/**
* Gets a value as a duration in a specified
* {@link java.util.concurrent.TimeUnit TimeUnit}. If the value is already a
* number, then it's taken as milliseconds and then converted to the
* requested TimeUnit; if it's a string, it's parsed understanding units
* suffixes like "10m" or "5ns" as documented in the the
* spec.
*
* @since 1.2.0
*
* @param path
* path expression
* @param unit
* convert the return value to this time unit
* @return the duration value at the requested path, in the given TimeUnit
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to Long or String
* @throws ConfigException.BadValue
* if value cannot be parsed as a number of the given TimeUnit
*/
long getDuration(String path, TimeUnit unit);
/**
* Gets a value as a java.time.Duration. If the value is
* already a number, then it's taken as milliseconds; if it's
* a string, it's parsed understanding units suffixes like
* "10m" or "5ns" as documented in the the
* spec. This method never returns null.
*
* @since 1.3.0
*
* @param path
* path expression
* @return the duration value at the requested path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to Long or String
* @throws ConfigException.BadValue
* if value cannot be parsed as a number of the given TimeUnit
*/
Duration getDuration(String path);
/**
* Gets a list value (with any element type) as a {@link ConfigList}, which
* implements {@code java.util.List}. Throws if the path is
* unset or null.
*
* @param path
* the path to the list value.
* @return the {@link ConfigList} at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a ConfigList
*/
ConfigList getList(String path);
/**
* Gets a list value with boolean elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to boolean.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of booleans
*/
List getBooleanList(String path);
/**
* Gets a list value with number elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to number.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of numbers
*/
List getNumberList(String path);
/**
* Gets a list value with int elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to int.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of ints
*/
List getIntList(String path);
/**
* Gets a list value with long elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to long.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of longs
*/
List getLongList(String path);
/**
* Gets a list value with double elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to double.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of doubles
*/
List getDoubleList(String path);
/**
* Gets a list value with string elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to string.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of strings
*/
List getStringList(String path);
/**
* Gets a list value with {@code Enum} elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to {@code Enum}.
*
* @param enumClass
* the enum class
* @param
* a generic denoting a specific type of enum
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of {@code Enum}
*/
> List getEnumList(Class enumClass, String path);
/**
* Gets a list value with object elements. Throws if the
* path is unset or null or not a list or contains values not
* convertible to ConfigObject.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of objects
*/
List extends ConfigObject> getObjectList(String path);
/**
* Gets a list value with Config elements.
* Throws if the path is unset or null or not a list or
* contains values not convertible to Config.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of configs
*/
List extends Config> getConfigList(String path);
/**
* Gets a list value with any kind of elements. Throws if the
* path is unset or null or not a list. Each element is
* "unwrapped" (see {@link ConfigValue#unwrapped()}).
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list
*/
List extends Object> getAnyRefList(String path);
/**
* Gets a list value with elements representing a size in
* bytes. Throws if the path is unset or null or not a list
* or contains values not convertible to memory sizes.
*
* @param path
* the path to the list value.
* @return the list at the path
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of memory sizes
*/
List getBytesList(String path);
/**
* Gets a list, converting each value in the list to a memory size, using the
* same rules as {@link #getMemorySize(String)}.
*
* @since 1.3.0
* @param path
* a path expression
* @return list of memory sizes
* @throws ConfigException.Missing
* if value is absent or null
* @throws ConfigException.WrongType
* if value is not convertible to a list of memory sizes
*/
List getMemorySizeList(String path);
/**
* @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)}
* @param path the path
* @return list of millisecond values
*/
@Deprecated List getMillisecondsList(String path);
/**
* @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)}
* @param path the path
* @return list of nanosecond values
*/
@Deprecated List getNanosecondsList(String path);
/**
* Gets a list, converting each value in the list to a duration, using the
* same rules as {@link #getDuration(String, TimeUnit)}.
*
* @since 1.2.0
* @param path
* a path expression
* @param unit
* time units of the returned values
* @return list of durations, in the requested units
*/
List getDurationList(String path, TimeUnit unit);
/**
* Gets a list, converting each value in the list to a duration, using the
* same rules as {@link #getDuration(String)}.
*
* @since 1.3.0
* @param path
* a path expression
* @return list of durations
*/
List getDurationList(String path);
/**
* Clone the config with only the given path (and its children) retained;
* all sibling paths are removed.
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* path to keep
* @return a copy of the config minus all paths except the one specified
*/
Config withOnlyPath(String path);
/**
* Clone the config with the given path removed.
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* path expression to remove
* @return a copy of the config minus the specified path
*/
Config withoutPath(String path);
/**
* Places the config inside another {@code Config} at the given path.
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* path expression to store this config at.
* @return a {@code Config} instance containing this config at the given
* path.
*/
Config atPath(String path);
/**
* Places the config inside a {@code Config} at the given key. See also
* atPath(). Note that a key is NOT a path expression (see
* {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param key
* key to store this config at.
* @return a {@code Config} instance containing this config at the given
* key.
*/
Config atKey(String key);
/**
* Returns a {@code Config} based on this one, but with the given path set
* to the given value. Does not modify this instance (since it's immutable).
* If the path already has a value, that value is replaced. To remove a
* value, use withoutPath().
*
* Note that path expressions have a syntax and sometimes require quoting
* (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
*
* @param path
* path expression for the value's new location
* @param value
* value at the new path
* @return the new instance with the new map entry
*/
Config withValue(String path, ConfigValue value);
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigBeanFactory.java 0000664 0000000 0000000 00000003335 12771472746 0026740 0 ustar 00root root 0000000 0000000 package com.typesafe.config;
import com.typesafe.config.impl.ConfigBeanImpl;
/**
* Factory for automatically creating a Java class from a {@link Config}.
* See {@link #create(Config,Class)}.
*
* @since 1.3.0
*/
public class ConfigBeanFactory {
/**
* Creates an instance of a class, initializing its fields from a {@link Config}.
*
* Example usage:
*
*
*
* The Java class should follow JavaBean conventions. Field types
* can be any of the types you can normally get from a {@link
* Config}, including java.time.Duration or {@link
* ConfigMemorySize}. Fields may also be another JavaBean-style
* class.
*
* Fields are mapped to config by converting the config key to
* camel case. So the key foo-bar becomes JavaBean
* setter setFooBar.
*
* @since 1.3.0
*
* @param config source of config information
* @param clazz class to be instantiated
* @param the type of the class to be instantiated
* @return an instance of the class populated with data from the config
* @throws ConfigException.BadBean
* If something is wrong with the JavaBean
* @throws ConfigException.ValidationFailed
* If the config doesn't conform to the bean's implied schema
* @throws ConfigException
* Can throw the same exceptions as the getters on Config
*/
public static T create(Config config, Class clazz) {
return ConfigBeanImpl.createInternal(config, clazz);
}
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigException.java 0000664 0000000 0000000 00000033205 12771472746 0026500 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import com.typesafe.config.impl.ConfigImplUtil;
/**
* All exceptions thrown by the library are subclasses of
* ConfigException.
*/
public abstract class ConfigException extends RuntimeException implements Serializable {
private static final long serialVersionUID = 1L;
final private transient ConfigOrigin origin;
protected ConfigException(ConfigOrigin origin, String message,
Throwable cause) {
super(origin.description() + ": " + message, cause);
this.origin = origin;
}
protected ConfigException(ConfigOrigin origin, String message) {
this(origin.description() + ": " + message, null);
}
protected ConfigException(String message, Throwable cause) {
super(message, cause);
this.origin = null;
}
protected ConfigException(String message) {
this(message, null);
}
/**
* Returns an "origin" (such as a filename and line number) for the
* exception, or null if none is available. If there's no sensible origin
* for a given exception, or the kind of exception doesn't meaningfully
* relate to a particular origin file, this returns null. Never assume this
* will return non-null, it can always return null.
*
* @return origin of the problem, or null if unknown/inapplicable
*/
public ConfigOrigin origin() {
return origin;
}
// we customize serialization because ConfigOrigin isn't
// serializable and we don't want it to be (don't want to
// support it)
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
ConfigImplUtil.writeOrigin(out, origin);
}
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
// circumvent "final"
Field f;
try {
f = ConfigException.class.getDeclaredField("origin");
} catch (NoSuchFieldException e) {
throw new IOException("ConfigException has no origin field?", e);
} catch (SecurityException e) {
throw new IOException("unable to fill out origin field in ConfigException", e);
}
f.setAccessible(true);
try {
f.set(this, origin);
} catch (IllegalArgumentException e) {
throw new IOException("unable to set origin field", e);
} catch (IllegalAccessException e) {
throw new IOException("unable to set origin field", e);
}
}
/**
* Exception indicating that the type of a value does not match the type you
* requested.
*
*/
public static class WrongType extends ConfigException {
private static final long serialVersionUID = 1L;
public WrongType(ConfigOrigin origin, String path, String expected, String actual,
Throwable cause) {
super(origin, path + " has type " + actual + " rather than " + expected, cause);
}
public WrongType(ConfigOrigin origin, String path, String expected, String actual) {
this(origin, path, expected, actual, null);
}
public WrongType(ConfigOrigin origin, String message, Throwable cause) {
super(origin, message, cause);
}
public WrongType(ConfigOrigin origin, String message) {
super(origin, message, null);
}
}
/**
* Exception indicates that the setting was never set to anything, not even
* null.
*/
public static class Missing extends ConfigException {
private static final long serialVersionUID = 1L;
public Missing(String path, Throwable cause) {
super("No configuration setting found for key '" + path + "'",
cause);
}
public Missing(String path) {
this(path, null);
}
protected Missing(ConfigOrigin origin, String message, Throwable cause) {
super(origin, message, cause);
}
protected Missing(ConfigOrigin origin, String message) {
this(origin, message, null);
}
}
/**
* Exception indicates that the setting was treated as missing because it
* was set to null.
*/
public static class Null extends Missing {
private static final long serialVersionUID = 1L;
private static String makeMessage(String path, String expected) {
if (expected != null) {
return "Configuration key '" + path
+ "' is set to null but expected " + expected;
} else {
return "Configuration key '" + path + "' is null";
}
}
public Null(ConfigOrigin origin, String path, String expected,
Throwable cause) {
super(origin, makeMessage(path, expected), cause);
}
public Null(ConfigOrigin origin, String path, String expected) {
this(origin, path, expected, null);
}
}
/**
* Exception indicating that a value was messed up, for example you may have
* asked for a duration and the value can't be sensibly parsed as a
* duration.
*
*/
public static class BadValue extends ConfigException {
private static final long serialVersionUID = 1L;
public BadValue(ConfigOrigin origin, String path, String message,
Throwable cause) {
super(origin, "Invalid value at '" + path + "': " + message, cause);
}
public BadValue(ConfigOrigin origin, String path, String message) {
this(origin, path, message, null);
}
public BadValue(String path, String message, Throwable cause) {
super("Invalid value at '" + path + "': " + message, cause);
}
public BadValue(String path, String message) {
this(path, message, null);
}
}
/**
* Exception indicating that a path expression was invalid. Try putting
* double quotes around path elements that contain "special" characters.
*
*/
public static class BadPath extends ConfigException {
private static final long serialVersionUID = 1L;
public BadPath(ConfigOrigin origin, String path, String message,
Throwable cause) {
super(origin,
path != null ? ("Invalid path '" + path + "': " + message)
: message, cause);
}
public BadPath(ConfigOrigin origin, String path, String message) {
this(origin, path, message, null);
}
public BadPath(String path, String message, Throwable cause) {
super(path != null ? ("Invalid path '" + path + "': " + message)
: message, cause);
}
public BadPath(String path, String message) {
this(path, message, null);
}
public BadPath(ConfigOrigin origin, String message) {
this(origin, null, message);
}
}
/**
* Exception indicating that there's a bug in something (possibly the
* library itself) or the runtime environment is broken. This exception
* should never be handled; instead, something should be fixed to keep the
* exception from occurring. This exception can be thrown by any method in
* the library.
*/
public static class BugOrBroken extends ConfigException {
private static final long serialVersionUID = 1L;
public BugOrBroken(String message, Throwable cause) {
super(message, cause);
}
public BugOrBroken(String message) {
this(message, null);
}
}
/**
* Exception indicating that there was an IO error.
*
*/
public static class IO extends ConfigException {
private static final long serialVersionUID = 1L;
public IO(ConfigOrigin origin, String message, Throwable cause) {
super(origin, message, cause);
}
public IO(ConfigOrigin origin, String message) {
this(origin, message, null);
}
}
/**
* Exception indicating that there was a parse error.
*
*/
public static class Parse extends ConfigException {
private static final long serialVersionUID = 1L;
public Parse(ConfigOrigin origin, String message, Throwable cause) {
super(origin, message, cause);
}
public Parse(ConfigOrigin origin, String message) {
this(origin, message, null);
}
}
/**
* Exception indicating that a substitution did not resolve to anything.
* Thrown by {@link Config#resolve}.
*/
public static class UnresolvedSubstitution extends Parse {
private static final long serialVersionUID = 1L;
public UnresolvedSubstitution(ConfigOrigin origin, String detail, Throwable cause) {
super(origin, "Could not resolve substitution to a value: " + detail, cause);
}
public UnresolvedSubstitution(ConfigOrigin origin, String detail) {
this(origin, detail, null);
}
}
/**
* Exception indicating that you tried to use a function that requires
* substitutions to be resolved, but substitutions have not been resolved
* (that is, {@link Config#resolve} was not called). This is always a bug in
* either application code or the library; it's wrong to write a handler for
* this exception because you should be able to fix the code to avoid it by
* adding calls to {@link Config#resolve}.
*/
public static class NotResolved extends BugOrBroken {
private static final long serialVersionUID = 1L;
public NotResolved(String message, Throwable cause) {
super(message, cause);
}
public NotResolved(String message) {
this(message, null);
}
}
/**
* Information about a problem that occurred in {@link Config#checkValid}. A
* {@link ConfigException.ValidationFailed} exception thrown from
* checkValid() includes a list of problems encountered.
*/
public static class ValidationProblem {
final private String path;
final private ConfigOrigin origin;
final private String problem;
public ValidationProblem(String path, ConfigOrigin origin, String problem) {
this.path = path;
this.origin = origin;
this.problem = problem;
}
/**
* Returns the config setting causing the problem.
* @return the path of the problem setting
*/
public String path() {
return path;
}
/**
* Returns where the problem occurred (origin may include info on the
* file, line number, etc.).
* @return the origin of the problem setting
*/
public ConfigOrigin origin() {
return origin;
}
/**
* Returns a description of the problem.
* @return description of the problem
*/
public String problem() {
return problem;
}
@Override
public String toString() {
return "ValidationProblem(" + path + "," + origin + "," + problem + ")";
}
}
/**
* Exception indicating that {@link Config#checkValid} found validity
* problems. The problems are available via the {@link #problems()} method.
* The getMessage() of this exception is a potentially very
* long string listing all the problems found.
*/
public static class ValidationFailed extends ConfigException {
private static final long serialVersionUID = 1L;
final private Iterable problems;
public ValidationFailed(Iterable problems) {
super(makeMessage(problems), null);
this.problems = problems;
}
public Iterable problems() {
return problems;
}
private static String makeMessage(Iterable problems) {
StringBuilder sb = new StringBuilder();
for (ValidationProblem p : problems) {
sb.append(p.origin().description());
sb.append(": ");
sb.append(p.path());
sb.append(": ");
sb.append(p.problem());
sb.append(", ");
}
if (sb.length() == 0)
throw new ConfigException.BugOrBroken(
"ValidationFailed must have a non-empty list of problems");
sb.setLength(sb.length() - 2); // chop comma and space
return sb.toString();
}
}
/**
* Some problem with a JavaBean we are trying to initialize.
* @since 1.3.0
*/
public static class BadBean extends BugOrBroken {
private static final long serialVersionUID = 1L;
public BadBean(String message, Throwable cause) {
super(message, cause);
}
public BadBean(String message) {
this(message, null);
}
}
/**
* Exception that doesn't fall into any other category.
*/
public static class Generic extends ConfigException {
private static final long serialVersionUID = 1L;
public Generic(String message, Throwable cause) {
super(message, cause);
}
public Generic(String message) {
this(message, null);
}
}
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigFactory.java 0000664 0000000 0000000 00000125326 12771472746 0026157 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
import com.typesafe.config.impl.ConfigImpl;
import com.typesafe.config.impl.Parseable;
import java.io.File;
import java.io.Reader;
import java.net.URL;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
/**
* 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.
*
*
You can find an example app and library on
* GitHub. Also be sure to read the package
* overview which describes the big picture as shown in those
* examples.
*/
public final class ConfigFactory {
private static final String STRATEGY_PROPERTY_NAME = "config.strategy";
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)}.
*
*
* To load a standalone resource (without the default reference and default
* overrides), use {@link #parseResourcesAnySyntax(String)} rather than this
* method. To load only the reference config use {@link #defaultReference()}
* and to load only the overrides use {@link #defaultOverrides()}.
*
* @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(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.
*
*
* To load a standalone resource (without the default reference and default
* overrides), use {@link #parseResourcesAnySyntax(ClassLoader, String)}
* rather than this method. To load only the reference config use
* {@link #defaultReference(ClassLoader)} and to load only the overrides use
* {@link #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
*/
public static Config load(ClassLoader loader, 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(String resourceBasename, ConfigParseOptions parseOptions,
ConfigResolveOptions resolveOptions) {
ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load");
Config appConfig = ConfigFactory.parseResourcesAnySyntax(resourceBasename, withLoader);
return load(withLoader.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(ClassLoader loader, String resourceBasename,
ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
return load(resourceBasename, parseOptions.setClassLoader(loader), resolveOptions);
}
private static ClassLoader checkedContextClassLoader(String methodName) {
ClassLoader 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
return loader;
}
private static ConfigParseOptions ensureClassLoader(ConfigParseOptions options, String methodName) {
if (options.getClassLoader() == null)
return options.setClassLoader(checkedContextClassLoader(methodName));
else
return 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
*/
public static Config load(Config config) {
return load(checkedContextClassLoader("load"), config);
}
/**
* Like {@link #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
*/
public static Config load(ClassLoader loader, 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(Config config, ConfigResolveOptions resolveOptions) {
return load(checkedContextClassLoader("load"), 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(ClassLoader loader, Config config, ConfigResolveOptions resolveOptions) {
return defaultOverrides(loader).withFallback(config).withFallback(defaultReference(loader))
.resolve(resolveOptions);
}
/**
* Loads a default configuration, equivalent to {@link #load(Config)
* 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 {@link #invalidateCaches()} to
* force it to reload.)
*
* @return configuration for an application
*/
public static Config load() {
ClassLoader loader = checkedContextClassLoader("load");
return load(loader);
}
/**
* Like {@link #load()} but allows specifying parse options.
*
* @param parseOptions
* Options for parsing resources
* @return configuration for an application
*/
public static Config load(ConfigParseOptions parseOptions) {
return load(parseOptions, ConfigResolveOptions.defaults());
}
/**
* 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) {
final ConfigParseOptions withLoader = ConfigParseOptions.defaults().setClassLoader(loader);
return ConfigImpl.computeCachedConfig(loader, "load", new Callable() {
@Override
public Config call() {
return load(loader, defaultApplication(withLoader));
}
});
}
/**
* Like {@link #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
*/
public static Config load(ClassLoader loader, ConfigParseOptions parseOptions) {
return load(parseOptions.setClassLoader(loader));
}
/**
* Like {@link #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
*/
public static Config load(ClassLoader loader, ConfigResolveOptions resolveOptions) {
return load(loader, ConfigParseOptions.defaults(), 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 (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
*/
public static Config load(ClassLoader loader, ConfigParseOptions parseOptions, ConfigResolveOptions resolveOptions) {
final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load");
return load(loader, defaultApplication(withLoader), resolveOptions);
}
/**
* Like {@link #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
*/
public static Config load(ConfigParseOptions parseOptions, final ConfigResolveOptions resolveOptions) {
final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load");
return 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
* {@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(checkedContextClassLoader("defaultReference"));
}
/**
* Like {@link #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
*/
public static Config defaultReference(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 class loader to look for resources in
* @return the default override configuration
*/
public static Config defaultOverrides(ClassLoader loader) {
return 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 {@link #load()} method automatically
* stacks the {@link #defaultReference()}, {@link
* #defaultApplication()}, and {@link #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 {@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, defaultApplication() is equivalent to
* ConfigFactory.parseResources("application").
*
* @since 1.3.0
*
* @return the default application.conf or system-property-configured configuration
*/
public static Config defaultApplication() {
return defaultApplication(ConfigParseOptions.defaults());
}
/**
* Like {@link #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
*/
public static Config defaultApplication(ClassLoader loader) {
return defaultApplication(ConfigParseOptions.defaults().setClassLoader(loader));
}
/**
* Like {@link #defaultApplication()} but allows you to specify parse options.
*
* @since 1.3.0
*
* @param options the options
* @return the default application configuration
*/
public static Config defaultApplication(ConfigParseOptions options) {
return getConfigLoadingStrategy().parseApplicationConfig(ensureClassLoader(options, "defaultApplication"));
}
/**
* 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(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
* the parse options
* @return the parsed configuration
*/
public static Config parseProperties(Properties properties,
ConfigParseOptions options) {
return Parseable.newProperties(properties, options).parse().toConfig();
}
/**
* Like {@link #parseProperties(Properties, ConfigParseOptions)} but uses default
* parse options.
* @param properties
* a Java Properties object
* @return the parsed configuration
*/
public static Config parseProperties(Properties properties) {
return parseProperties(properties, ConfigParseOptions.defaults());
}
/**
* Parses a Reader into a Config instance. Does not call
* {@link 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
*/
public static Config parseReader(Reader reader, ConfigParseOptions options) {
return Parseable.newReader(reader, options).parse().toConfig();
}
/**
* Parses a reader into a Config instance as with
* {@link #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
*/
public static Config parseReader(Reader reader) {
return parseReader(reader, ConfigParseOptions.defaults());
}
/**
* Parses a URL into a Config instance. Does not call
* {@link 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
*/
public static Config parseURL(URL url, ConfigParseOptions options) {
return Parseable.newURL(url, options).parse().toConfig();
}
/**
* Parses a url into a Config instance as with
* {@link #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
*/
public static Config parseURL(URL url) {
return parseURL(url, ConfigParseOptions.defaults());
}
/**
* Parses a file into a Config instance. Does not call
* {@link 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
*/
public static Config parseFile(File file, ConfigParseOptions options) {
return Parseable.newFile(file, options).parse().toConfig();
}
/**
* Parses a file into a Config instance as with
* {@link #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
*/
public static Config parseFile(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(File fileBasename,
ConfigParseOptions options) {
return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig();
}
/**
* Like {@link #parseFileAnySyntax(File,ConfigParseOptions)} but always uses
* default parse options.
*
* @param fileBasename
* a filename with or without extension
* @return the parsed configuration
*/
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 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(Class> klass, String resource,
ConfigParseOptions options) {
return Parseable.newResources(klass, resource, options).parse()
.toConfig();
}
/**
* Like {@link #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
*/
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
* {@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(Class> klass, String resourceBasename,
ConfigParseOptions options) {
return ConfigImpl.parseResourcesAnySyntax(klass, resourceBasename,
options).toConfig();
}
/**
* Like {@link #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 {@link java.lang.Class#getResource},
* with or without extension
* @return the parsed configuration
*/
public static Config parseResourcesAnySyntax(Class> klass, 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(ClassLoader loader, String resource,
ConfigParseOptions options) {
return parseResources(resource, options.setClassLoader(loader));
}
/**
* Like {@link #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
*/
public static Config parseResources(ClassLoader loader, 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(ClassLoader loader, String resourceBasename,
ConfigParseOptions options) {
return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options.setClassLoader(loader))
.toConfig();
}
/**
* Like {@link #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
* {@link java.lang.ClassLoader#getResource}, with or without
* extension
* @return the parsed configuration
*/
public static Config parseResourcesAnySyntax(ClassLoader loader, String resourceBasename) {
return parseResourcesAnySyntax(loader, resourceBasename, ConfigParseOptions.defaults());
}
/**
* Like {@link #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
*/
public static Config parseResources(String resource, ConfigParseOptions options) {
ConfigParseOptions withLoader = ensureClassLoader(options, "parseResources");
return Parseable.newResources(resource, withLoader).parse().toConfig();
}
/**
* Like {@link #parseResources(ClassLoader,String)} but uses thread's
* current context class loader.
* @param resource the resource name
* @return the parsed configuration
*/
public static Config parseResources(String resource) {
return parseResources(resource, ConfigParseOptions.defaults());
}
/**
* Like
* {@link #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
*/
public static Config parseResourcesAnySyntax(String resourceBasename, ConfigParseOptions options) {
return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options).toConfig();
}
/**
* Like {@link #parseResourcesAnySyntax(ClassLoader,String)} but uses
* thread's current context class loader.
* @param resourceBasename the resource basename (no file type suffix)
* @return the parsed configuration
*/
public static Config parseResourcesAnySyntax(String resourceBasename) {
return 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
*/
public static Config parseString(String s, ConfigParseOptions options) {
return 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
*/
public static Config parseString(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 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 {@code Config}
*/
public static Config parseMap(Map values,
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 map from paths to plain Java values
* @return the map converted to a {@code Config}
*/
public static Config parseMap(Map values) {
return parseMap(values, null);
}
private static ConfigLoadingStrategy getConfigLoadingStrategy() {
String className = System.getProperties().getProperty(STRATEGY_PROPERTY_NAME);
if (className != null) {
try {
return ConfigLoadingStrategy.class.cast(Class.forName(className).newInstance());
} catch (Throwable e) {
throw new ConfigException.BugOrBroken("Failed to load strategy: " + className, e);
}
} else {
return new DefaultConfigLoadingStrategy();
}
}
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigIncludeContext.java 0000664 0000000 0000000 00000003752 12771472746 0027476 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
/**
* Context provided to a {@link ConfigIncluder}; this interface is only useful
* inside a {@code ConfigIncluder} implementation, and is not intended for apps
* to implement.
*
*
* Do not implement this interface; it should only be implemented by
* the config library. Arbitrary implementations will not work because the
* library internals assume a specific concrete implementation. Also, this
* interface is likely to grow new methods over time, so third-party
* implementations will break.
*/
public interface ConfigIncludeContext {
/**
* Tries to find a name relative to whatever is doing the including, for
* example in the same directory as the file doing the including. Returns
* null if it can't meaningfully create a relative name. The returned
* parseable may not exist; this function is not required to do any IO, just
* compute what the name would be.
*
* The passed-in filename has to be a complete name (with extension), not
* just a basename. (Include statements in config files are allowed to give
* just a basename.)
*
* @param filename
* the name to make relative to the resource doing the including
* @return parseable item relative to the resource doing the including, or
* null
*/
ConfigParseable relativeTo(String filename);
/**
* Parse options to use (if you use another method to get a
* {@link ConfigParseable} then use {@link ConfigParseable#options()}
* instead though).
*
* @return the parse options
*/
ConfigParseOptions parseOptions();
/**
* Copy this {@link ConfigIncludeContext} giving it a new value for its parseOptions.
*
* @param options new parse options to use
*
* @return the updated copy of this context
*/
ConfigIncludeContext setParseOptions(ConfigParseOptions options);
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigIncluder.java 0000664 0000000 0000000 00000004213 12771472746 0026304 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
/**
* Implement this interface and provide an instance to
* {@link ConfigParseOptions#setIncluder ConfigParseOptions.setIncluder()} to
* customize handling of {@code include} statements in config files. You may
* also want to implement {@link ConfigIncluderClasspath},
* {@link ConfigIncluderFile}, and {@link ConfigIncluderURL}, or not.
*/
public interface ConfigIncluder {
/**
* Returns a new includer that falls back to the given includer. This is how
* you can obtain the default includer; it will be provided as a fallback.
* It's up to your includer to chain to it if you want to. You might want to
* merge any files found by the fallback includer with any objects you load
* yourself.
*
* It's important to handle the case where you already have the fallback
* with a "return this", i.e. this method should not create a new object if
* the fallback is the same one you already have. The same fallback may be
* added repeatedly.
*
* @param fallback the previous includer for chaining
* @return a new includer
*/
ConfigIncluder withFallback(ConfigIncluder fallback);
/**
* Parses another item to be included. The returned object typically would
* not have substitutions resolved. You can throw a ConfigException here to
* abort parsing, or return an empty object, but may not return null.
*
* This method is used for a "heuristic" include statement that does not
* specify file, URL, or classpath resource. If the include statement does
* specify, then the same class implementing {@link ConfigIncluder} must
* also implement {@link ConfigIncluderClasspath},
* {@link ConfigIncluderFile}, or {@link ConfigIncluderURL} as needed, or a
* default includer will be used.
*
* @param context
* some info about the include context
* @param what
* the include statement's argument
* @return a non-null ConfigObject
*/
ConfigObject include(ConfigIncludeContext context, String what);
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigIncluderClasspath.java 0000664 0000000 0000000 00000001730 12771472746 0030150 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
/**
* Implement this in addition to {@link ConfigIncluder} if you want to
* support inclusion of files with the {@code include classpath("resource")}
* syntax. If you do not implement this but do implement {@link ConfigIncluder},
* attempts to load classpath resources will use the default includer.
*/
public interface ConfigIncluderClasspath {
/**
* Parses another item to be included. The returned object typically would
* not have substitutions resolved. You can throw a ConfigException here to
* abort parsing, or return an empty object, but may not return null.
*
* @param context
* some info about the include context
* @param what
* the include statement's argument
* @return a non-null ConfigObject
*/
ConfigObject includeResources(ConfigIncludeContext context, String what);
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigIncluderFile.java 0000664 0000000 0000000 00000001717 12771472746 0027112 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
import java.io.File;
/**
* Implement this in addition to {@link ConfigIncluder} if you want to
* support inclusion of files with the {@code include file("filename")} syntax.
* If you do not implement this but do implement {@link ConfigIncluder},
* attempts to load files will use the default includer.
*/
public interface ConfigIncluderFile {
/**
* Parses another item to be included. The returned object typically would
* not have substitutions resolved. You can throw a ConfigException here to
* abort parsing, or return an empty object, but may not return null.
*
* @param context
* some info about the include context
* @param what
* the include statement's argument
* @return a non-null ConfigObject
*/
ConfigObject includeFile(ConfigIncludeContext context, File what);
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigIncluderURL.java 0000664 0000000 0000000 00000001724 12771472746 0026673 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
import java.net.URL;
/**
* Implement this in addition to {@link ConfigIncluder} if you want to
* support inclusion of files with the {@code include url("http://example.com")}
* syntax. If you do not implement this but do implement {@link ConfigIncluder},
* attempts to load URLs will use the default includer.
*/
public interface ConfigIncluderURL {
/**
* Parses another item to be included. The returned object typically would
* not have substitutions resolved. You can throw a ConfigException here to
* abort parsing, or return an empty object, but may not return null.
*
* @param context
* some info about the include context
* @param what
* the include statement's argument
* @return a non-null ConfigObject
*/
ConfigObject includeURL(ConfigIncludeContext context, URL what);
}
config-1.3.1/config/src/main/java/com/typesafe/config/ConfigList.java 0000664 0000000 0000000 00000003055 12771472746 0025455 0 ustar 00root root 0000000 0000000 /**
* Copyright (C) 2011-2012 Typesafe Inc.
*/
package com.typesafe.config;
import java.util.List;
/**
* Subtype of {@link ConfigValue} representing a list value, as in JSON's
* {@code [1,2,3]} syntax.
*
*
* {@code ConfigList} implements {@code java.util.List} so you can
* use it like a regular Java list. Or call {@link #unwrapped()} to unwrap the
* list elements into plain Java values.
*
*
* Like all {@link ConfigValue} subtypes, {@code ConfigList} is immutable. This
* makes it threadsafe and you never have to create "defensive copies." The
* mutator methods from {@link java.util.List} all throw
* {@link java.lang.UnsupportedOperationException}.
*
*
* The {@link ConfigValue#valueType} method on a list returns
* {@link ConfigValueType#LIST}.
*
*
* Do not implement {@code ConfigList}; it should only be implemented
* by the config library. Arbitrary implementations will not work because the
* library internals assume a specific concrete implementation. Also, this
* interface is likely to grow new methods over time, so third-party
* implementations will break.
*
*/
public interface ConfigList extends List, ConfigValue {
/**
* Recursively unwraps the list, returning a list of plain Java values such
* as Integer or String or whatever is in the list.
*
* @return a {@link java.util.List} containing plain Java objects
*/
@Override
List