diff --git a/.travis.yml b/.travis.yml
index d957beebf..980ed49e4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,25 +1,32 @@
# 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
+before_install: curl -Ls https://git.io/jabba | bash && . ~/.jabba/jabba.sh
+install: jabba install "adopt@~1.$TRAVIS_JDK.0-0" && jabba use "$_" && java -Xmx32m -version
-script:
- - sbt ++$TRAVIS_SCALA_VERSION test doc
+jobs:
+ include:
+ - stage: jdk8
+ name: "test and docs on jdk8"
+ script: sbt ++$TRAVIS_SCALA_VERSION test doc
+ - stage: jdk11
+ name: "test and docs on jdk11"
+ script: sbt ++$TRAVIS_SCALA_VERSION test doc
+ env: TRAVIS_JDK=11
+env:
+ global:
+ - TRAVIS_JDK=8
+
+before_cache:
# Remove to avoid unnecessary cache updates
- find $HOME/.sbt -name "*.lock" -delete
- find $HOME/.ivy2 -name "ivydata-*.properties" -delete
+
+cache:
+ directories:
+ - $HOME/.ivy2/cache
+ - $HOME/.sbt/boot
+ - $HOME/.jabba/jdk
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a3227a333..255892ab2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,7 +14,7 @@ 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
+https://www.lightbend.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
@@ -23,38 +23,4 @@ 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. update README with the new version.
- 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
- (under Staging Repositories, 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
+See RELEASING.md
diff --git a/HOCON.md b/HOCON.md
index 168a0a33c..9f8bd837d 100644
--- a/HOCON.md
+++ b/HOCON.md
@@ -109,7 +109,7 @@ the config file to the computer program.
## Syntax
Much of this is defined with reference to JSON; you can find the
-JSON spec at http://json.org/ of course.
+JSON spec at https://json.org/ of course.
### Unchanged from JSON
@@ -317,11 +317,6 @@ 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
@@ -739,9 +734,6 @@ 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
@@ -959,11 +951,6 @@ 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
@@ -1296,7 +1283,27 @@ must be lowercase. Exactly these strings are supported:
- `m`, `minute`, `minutes`
- `h`, `hour`, `hours`
- `d`, `day`, `days`
+
+### Period Format
+
+Similar to the `getDuration()` method, there is a `getPeriod()` method
+available for getting time units as a `java.time.Period`.
+This can use the general "units format" described above; bare
+numbers are taken to be in days, while strings are
+parsed as a number plus an optional unit string.
+
+The supported unit strings for period are case sensitive and
+must be lowercase. Exactly these strings are supported:
+
+ - `d`, `day`, `days`
+ - `w`, `week`, `weeks`
+ - `m`, `mo`, `month`, `months` (note that if you are using `getTemporal()`
+ which may return either a `java.time.Duration` or a `java.time.Period`
+ you will want to use `mo` rather than `m` to prevent your unit being
+ parsed as minutes)
+ - `y`, `year`, `years`
+
### Size in bytes format
Implementations may wish to support a `getBytes()` returning a
@@ -1317,7 +1324,7 @@ 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
+https://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.
@@ -1407,7 +1414,7 @@ way to get rid of default fallback values they don't want.
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
+https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#load-java.io.Reader-
Java properties parse as a one-level map from string keys to
string values.
diff --git a/LICENSE-2.0.txt b/LICENSE-2.0.txt
index d64569567..62589edd1 100644
--- a/LICENSE-2.0.txt
+++ b/LICENSE-2.0.txt
@@ -1,7 +1,7 @@
Apache License
Version 2.0, January 2004
- http://www.apache.org/licenses/
+ https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -193,7 +193,7 @@
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
+ https://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,
diff --git a/NEWS.md b/NEWS.md
index 22d4cfb65..aeb66d0d5 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,41 @@
+# 1.4.0: October 11, 2019
+
+- `application.conf` variables can now override variables in `reference.conf` [#619](https://github.com/lightbend/config/issues/167)
+- performance improvement through capacity hint for `ArrayList` [#648](https://github.com/lightbend/config/pull/648)
+
+(This was previously published as 1.3.5-RC1 but deemed a large enough change to require a minor version rather than a patch)
+
+# 1.3.4: April 18, 2019
+
+- it is now possible to override any configuration setting from environment variables ([#620](/../../pull/620)) thanks to [@andreaTP](https://github.com/andreaTP)
+- added support for integer keys that are longer than `Int` ([#568](/../../pull/568)) thanks to [@michalmela](https://github.com/michalmela)
+- `Missing` exception now has a reference to the origin `Config` ([#585](/../../pull/585)) thanks to [@vagola](https://github.com/vagola)
+- performance improvements to `resolve()` ([#578](/../../pull/578)) thanks to [@sam-token](https://github.com/sam-token)
+- config file syntax is now resolved when parsing `InputStream` ([#573](/../../pull/573)) thanks to [@ntviet18](https://github.com/ntviet18)
+- it is now possible to use `@Optional` on keys that are reserved words ([#570](/../../pull/570)) thanks to [@radist-nt](https://github.com/radist-nt)
+- `ValidationProblem` is now serializable ([#437](/../../pull/437)) thanks to [@iantabolt](https://github.com/iantabolt)
+
+All of the merged PRs can be found in the [release milestone](https://github.com/lightbend/config/milestone/4?closed=1).
+
+# 1.3.3: February 22, 2018
+
+- artifact now includes `Automatic-Module-Name` which makes it consumable as Java 9 module.
+- minor issue fix. All of the fixed issues can be found in the [milestone page](https://github.com/lightbend/config/milestone/3?closed=1).
+
+# 1.3.2: October 6, 2017
+
+- environment variables are now able to be resolved to lists in
+ the same fashion as system properties.
+- added `getPeriod()` which returns time units as
+ `java.time.Period`. Currently supported periods are days, weeks,
+ months and years. [More information here](HOCON.md#period-format).
+- `ConfigResolveOptions` now has `appendResolver(...)` which allows
+ having custom behavior when unresolved substitutions are encountered
+ during resolution.
+- Config Beans now support `Set` collection.
+- a few other small bugfixes. All of the fixed issues can be found
+ in the [milestone page](https://github.com/lightbend/config/milestone/1?closed=1).
+
# 1.3.1: September 24, 2016
- added `include required("foo")` syntax to specify includes that
@@ -208,7 +246,7 @@ Thank you to contributors with commits since v1.2.1 tag:
- 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/
+ https://lightbend.github.io/config/latest/api/
# 1.1.0-9f31d6308e7ebbc3d7904b64ebb9f61f7e22a968: January 6, 2014
@@ -226,7 +264,7 @@ Thank you to contributors with commits since v1.2.1 tag:
- 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 )
+ https://github.com/lightbend/config/issues/119 )
- new API ConfigResolveOptions.setAllowUnresolved lets you
partially-resolve a Config
- new API Config.isResolved lets you check on resolution status
diff --git a/README.md b/README.md
index b291be549..e2f29a466 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
Configuration library for JVM languages.
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.typesafe/config/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.typesafe/config)
-[![Build Status](https://travis-ci.org/typesafehub/config.svg?branch=master)](https://travis-ci.org/typesafehub/config)
+[![Build Status](https://travis-ci.org/lightbend/config.svg?branch=master)](https://travis-ci.org/lightbend/config)
If you have questions or are working on a pull request or just
curious, please feel welcome to join the chat room:
-[![Join chat https://gitter.im/typesafehub/config](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/typesafehub/config?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Join chat https://gitter.im/lightbend/config](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/lightbend/config?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Overview
@@ -71,7 +71,6 @@ to merge it in.
- [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)
@@ -88,16 +87,14 @@ to merge it in.
- [Python port](#python-port)
- [C++ port](#c-port)
- [JavaScript port](#javascript-port)
+ - [C# port](#c-port-1)
- [Linting tool](#linting-tool)
+ - [Online playground](#online-playground)
## 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
@@ -108,12 +105,12 @@ You can find published releases on Maven Central.
com.typesafeconfig
- 1.3.1
+ 1.4.0
sbt dependency:
- libraryDependencies += "com.typesafe" % "config" % "1.3.1"
+ libraryDependencies += "com.typesafe" % "config" % "1.4.0"
Link for direct download if you don't use a dependency manager:
@@ -122,15 +119,15 @@ Link for direct download if you don't use a dependency manager:
### Release Notes
Please see NEWS.md in this directory,
-https://github.com/typesafehub/config/blob/master/NEWS.md
+https://github.com/lightbend/config/blob/master/NEWS.md
### API docs
- - Online: http://typesafehub.github.com/config/latest/api/
+ - Online: https://lightbend.github.io/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)
+ [HOCON.md](https://github.com/lightbend/config/blob/master/HOCON.md)
in this directory
### Bugs and Patches
@@ -141,10 +138,10 @@ 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
+https://www.lightbend.com/contribute/cla
Please see
-[CONTRIBUTING](https://github.com/typesafehub/config/blob/master/CONTRIBUTING.md)
+[CONTRIBUTING](https://github.com/lightbend/config/blob/master/CONTRIBUTING.md)
for more including how to make a release.
### Build
@@ -165,7 +162,7 @@ Scala dependency.
### Longer Examples
-See the examples in the `examples/` [directory](https://github.com/typesafehub/config/tree/master/examples).
+See the examples in the `examples/` [directory](https://github.com/lightbend/config/tree/master/examples).
You can run these from the sbt console with the commands `project
config-simple-app-java` and then `run`.
@@ -189,7 +186,7 @@ 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
+[API docs](https://lightbend.github.io/config/latest/api/) for
details of course.
### Schemas and Validation
@@ -198,7 +195,7 @@ There isn't a schema language or anything like that. However, two
suggested tools are:
- use the
- [checkValid() method](http://typesafehub.github.io/config/latest/api/com/typesafe/config/Config.html#checkValid-com.typesafe.config.Config-java.lang.String...-)
+ [checkValid() method](https://lightbend.github.io/config/latest/api/com/typesafe/config/Config.html#checkValid-com.typesafe.config.Config-java.lang.String...-)
- 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)
@@ -235,9 +232,6 @@ The idea is that libraries and frameworks should ship with a
`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
@@ -282,23 +276,14 @@ system properties.
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.
+layered over the unresolved `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.
+to be provided by `application.conf`. It is however possible to
+override a variable that `reference.conf` refers to, as long as
+`reference.conf` also defines that variable itself.
### Merging config trees
@@ -435,7 +420,7 @@ 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)
+[Config](https://lightbend.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:
@@ -450,7 +435,7 @@ this:
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)
+([HOCON.md](https://github.com/lightbend/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
@@ -463,7 +448,7 @@ 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)
+[ConfigObject](https://lightbend.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
@@ -475,15 +460,15 @@ expressions). Iterating over the above example as a
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
+`ConfigObject` is a subtype of [ConfigValue](https://lightbend.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)
+[Config.root()](https://lightbend.github.io/config/latest/api/com/typesafe/config/Config.html#root--)
and
-[ConfigObject.toConfig()](http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigObject.html#toConfig%28%29).
+[ConfigObject.toConfig()](https://lightbend.github.io/config/latest/api/com/typesafe/config/ConfigObject.html#toConfig--).
### ConfigBeanFactory
@@ -506,7 +491,7 @@ particular value manually).
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)
+[HOCON.md](https://github.com/lightbend/config/blob/master/HOCON.md)
in this directory for more detail.
After processing a `.conf` file, the result is always just a JSON
@@ -680,6 +665,24 @@ value just disappear if the substitution is not found:
// this array could have one or two elements
path = [ "a", ${?OPTIONAL_A} ]
+By setting the JVM property `-Dconfig.override_with_env_vars=true`
+it is possible to override any configuration value using environment
+variables even if an explicit substitution is not specified.
+
+The environment variable value will override any pre-existing value
+and also any value provided as Java property.
+
+With this option enabled only environment variables starting with
+`CONFIG_FORCE_` are considered, and the name is mangled as follows:
+
+ - the prefix `CONFIG_FORCE_` is stripped
+ - single underscore(`_`) is converted into a dot(`.`)
+ - double underscore(`__`) is converted into a dash(`-`)
+ - triple underscore(`___`) is converted into a single underscore(`_`)
+
+i.e. The environment variable `CONFIG_FORCE_a_b__c___d` set the
+configuration key `a.b-c_d`
+
### Concatenation
Values _on the same line_ are concatenated (for strings and
@@ -693,9 +696,12 @@ string `foo` are concatenated into a string `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:
+Quoted or unquoted strings can also concatenate with substitutions of course:
tasks-url : ${base-url}/tasks
+ tasks-url : ${base-url}"tasks:colon-must-be-quoted"
+
+Note: the `${}` syntax must be outside the quotes!
A concatenation can refer to earlier values of the same field:
@@ -742,14 +748,6 @@ 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
@@ -760,8 +758,19 @@ If you have trouble with your configuration, some useful tips.
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
+ - Use `myConfig.root().render()` to get a `Config` as a
string with comments showing where each value came from.
+ This string can be printed out on console or logged to
+ a file etc.
+ - If you see errors like
+ `com.typesafe.config.ConfigException$Missing: No configuration setting found for key foo`,
+ and you're sure that key is defined in your config file, they might appear e.g.
+ when you're loading configuration from a thread that's not the JVM's main thread.
+ Try passing the `ClassLoader` in manually - e.g. with `ConfigFactory.load(getClass().getClassLoader())`
+ or setting the context class loader.
+ If you don't pass one, Lightbend Config uses the calling thread's `contextClassLoader`, and in some cases,
+ it may not have your configuration files in its classpath,
+ so loading the config on that thread can yield unexpected, erroneous results.
### Supports Java 8 and Later
@@ -771,7 +780,7 @@ 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.x 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
+on [chat](https://gitter.im/lightbend/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.
@@ -840,7 +849,7 @@ format.
* Ficus https://github.com/ceedubs/ficus
* configz https://github.com/arosien/configz
* configs https://github.com/kxbmap/configs
- * config-annotation https://github.com/zhongl/config-annotation
+ * config-annotation https://github.com/zhongl/config-annotation
* PureConfig https://github.com/pureconfig/pureconfig
* Simple Scala Config https://github.com/ElderResearch/ssc
* konfig https://github.com/vpon/konfig
@@ -849,17 +858,20 @@ format.
* validated-config https://github.com/carlpulley/validated-config
* Cedi Config https://github.com/ccadllc/cedi-config
* Cfg https://github.com/carueda/cfg
+ * circe-config https://github.com/circe/circe-config
+ * args4c https://github.com/aaronp/args4c
#### Clojure wrappers for the Java library
* beamly-core.config https://github.com/beamly/beamly-core.config
-
+
#### Kotlin wrappers for the Java library
* config4k https://github.com/config4k/config4k
-
+
#### Scala port
- * SHocon https://github.com/unicredit/shocon (work with both Scala and Scala.Js)
+ * SHocon https://github.com/akka-js/shocon (Supports Scala.js and Scala Native)
+ * sconfig https://github.com/ekrich/sconfig (Supports JVM, Scala Native, and Scala.js)
#### Ruby port
@@ -878,9 +890,33 @@ format.
* https://github.com/puppetlabs/cpp-hocon
#### JavaScript port
-
+
* https://github.com/yellowblood/hocon-js (missing features, under development)
-
+
+#### C# port
+
+ * https://github.com/akkadotnet/HOCON
+
+#### Rust port
+
+ * https://github.com/mockersf/hocon.rs
+
#### Linting tool
* A web based linting tool http://www.hoconlint.com/
+
+#### Online playground
+
+ * https://hocon-playground.herokuapp.com/
+
+# Maintenance notes
+
+## License
+
+The license is Apache 2.0, see LICENSE-2.0.txt.
+
+## Maintained by
+
+This project is maintained mostly by [@havocp](https://github.com/havocp) and [@akka-team](https://github.com/orgs/lightbend/teams/akka-team/members).
+
+Feel free to ping above maintainers for code review or discussions. Pull requests are very welcome–thanks in advance!
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 000000000..08d4723b1
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,37 @@
+# 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. update README with the new version.
+ 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
+ (under Staging Repositories, 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
diff --git a/build.sbt b/build.sbt
index 65be9256f..631d5c8d9 100644
--- a/build.sbt
+++ b/build.sbt
@@ -2,21 +2,178 @@
// update NEWS, update version in README.md, tag, then
// publishSigned.
// Release tags should follow: http://semver.org/
+import scalariform.formatter.preferences._
-enablePlugins(GitVersioning)
-git.baseVersion := "1.3.0"
+ThisBuild / git.baseVersion := "1.4.0"
+ThisBuild / organization := "com.typesafe"
+ThisBuild / Compile / scalacOptions := List("-unchecked", "-deprecation", "-feature")
+ThisBuild / Test / scalacOptions := List("-unchecked", "-deprecation", "-feature")
+ThisBuild / scalaVersion := "2.12.8"
-organization in GlobalScope := "com.typesafe"
+ThisBuild / scmInfo := Option(
+ ScmInfo(url("https://github.com/lightbend/config"), "scm:git@github.com:lightbend/config.git")
+)
+ThisBuild / developers := List(
+ Developer(
+ id = "havocp",
+ name = "Havoc Pennington",
+ email = "@havocp",
+ url = url("http://ometer.com/")
+ )
+)
+ThisBuild / description := "configuration library for JVM languages using HOCON files"
+ThisBuild / licenses := List("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0"))
+ThisBuild / homepage := Option(url("https://github.com/lightbend/config"))
+ThisBuild / pomIncludeRepository := { _ => false }
+ThisBuild / publishTo := {
+ val nexus = "https://oss.sonatype.org/"
+ if ((ThisBuild / isSnapshot).value) Option("Sonatype OSS Snapshots" at nexus + "content/repositories/snapshots")
+ else Option("Sonatype OSS Staging" at nexus + "service/local/staging/deploy/maven2")
+}
+ThisBuild / publishMavenStyle := true
-scalacOptions in GlobalScope in Compile := Seq("-unchecked", "-deprecation", "-feature")
-scalacOptions in GlobalScope in Test := Seq("-unchecked", "-deprecation", "-feature")
+lazy val root = (project in file("."))
+ .enablePlugins(GitVersioning)
+ .aggregate(
+ testLib, configLib,
+ simpleLibScala, simpleAppScala, complexAppScala,
+ simpleLibJava, simpleAppJava, complexAppJava
+ )
+ .settings(commonSettings)
+ .settings(nocomma {
+ name := "config-root"
+ git.baseVersion := (ThisBuild / git.baseVersion).value
+ doc / aggregate := false
+ doc := (configLib / Compile / doc).value
+ packageDoc / aggregate := false
+ packageDoc := (configLib / Compile / packageDoc).value
+ checkstyle / aggregate := false
+ checkstyle := (configLib / Compile / checkstyle).value
+ useGpg := true
+ PgpKeys.publishSigned / aggregate := false
+ PgpKeys.publishSigned := (PgpKeys.publishSigned in configLib).value
+ PgpKeys.publishLocalSigned / aggregate := false
+ PgpKeys.publishLocalSigned := (PgpKeys.publishLocalSigned in configLib).value
+ })
-scalaVersion in ThisBuild := "2.10.4"
+lazy val configLib = Project("config", file("config"))
+ .enablePlugins(SbtOsgi)
+ .dependsOn(testLib % "test->test")
+ .settings(osgiSettings)
+ .settings(nocomma {
+ autoScalaLibrary := false
+ crossPaths := false
+ libraryDependencies += "net.liftweb" %% "lift-json" % "3.3.0" % Test
+ libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test
-useGpg := true
+ Compile / compile / javacOptions ++= Seq("-source", "1.8", "-target", "1.8",
+ "-g", "-Xlint:unchecked")
-aggregate in PgpKeys.publishSigned := false
-PgpKeys.publishSigned := (PgpKeys.publishSigned in configLib).value
+ Compile / doc / javacOptions ++= 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/lightbend/config/blob/$gitHead/config/src/main/java"
+ }
+ // because we test some global state such as singleton caches,
+ // we have to run tests in serial.
+ Test / parallelExecution := false
-aggregate in PgpKeys.publishLocalSigned := false
-PgpKeys.publishLocalSigned := (PgpKeys.publishLocalSigned in configLib).value
+ test / fork := true
+ Test / fork := true
+ run / fork := true
+ Test/ run / fork := true
+
+ //env vars for tests
+ Test / envVars ++= Map("testList.0" -> "0",
+ "testList.1" -> "1",
+ "CONFIG_FORCE_b" -> "5",
+ "CONFIG_FORCE_testList_0" -> "10",
+ "CONFIG_FORCE_testList_1" -> "11",
+ "CONFIG_FORCE_42___a" -> "1",
+ "CONFIG_FORCE_a_b_c" -> "2",
+ "CONFIG_FORCE_a__c" -> "3",
+ "CONFIG_FORCE_a___c" -> "4",
+ "CONFIG_FORCE_akka_version" -> "foo",
+ "CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10")
+
+ OsgiKeys.exportPackage := Seq("com.typesafe.config", "com.typesafe.config.impl")
+ publish := sys.error("use publishSigned instead of plain publish")
+ publishLocal := sys.error("use publishLocalSigned instead of plain publishLocal")
+ Compile / packageBin / packageOptions +=
+ Package.ManifestAttributes("Automatic-Module-Name" -> "typesafe.config" )
+ scalariformPreferences := scalariformPreferences.value
+ .setPreference(IndentSpaces, 4)
+ .setPreference(FirstArgumentOnNewline, Preserve)
+
+ checkstyleConfigLocation := CheckstyleConfigLocation.File((baseDirectory.value / "checkstyle-config.xml").toString)
+
+ Compile / checkstyle := {
+ val log = streams.value.log
+ (Compile / checkstyle).value
+ val resultFile = (Compile / checkstyleOutputFile).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
+ Compile / doc := ((Compile / doc).dependsOn(Compile / checkstyle)).value
+
+ findbugsReportType := Some(FindbugsReport.Html)
+ findbugsReportPath := Some(crossTarget.value / "findbugs.html")
+ findbugsEffort := FindbugsEffort.Maximum
+ findbugsMaxMemory := 2000
+ })
+
+lazy val commonSettings: Seq[Setting[_]] = Def.settings(
+ unpublished,
+ scalariformPreferences := scalariformPreferences.value
+ .setPreference(IndentSpaces, 4)
+ .setPreference(FirstArgumentOnNewline, Preserve),
+ Compile / compile / javacOptions ++= Seq("-source", "1.8", "-target", "1.8"),
+)
+
+def proj(id: String, base: File) = Project(id, base) settings commonSettings
+
+lazy val testLib = proj("config-test-lib", file("test-lib"))
+
+lazy val simpleLibScala = proj("config-simple-lib-scala", file("examples/scala/simple-lib")) dependsOn configLib
+lazy val simpleAppScala = proj("config-simple-app-scala", file("examples/scala/simple-app")) dependsOn simpleLibScala
+lazy val complexAppScala = proj("config-complex-app-scala", file("examples/scala/complex-app")) dependsOn simpleLibScala
+
+lazy val simpleLibJava = proj("config-simple-lib-java", file("examples/java/simple-lib")) dependsOn configLib
+lazy val simpleAppJava = proj("config-simple-app-java", file("examples/java/simple-app")) dependsOn simpleLibJava
+lazy val complexAppJava = proj("config-complex-app-java", file("examples/java/complex-app")) dependsOn simpleLibJava
+
+val unpublished = Seq(
+ // no artifacts in this project
+ publishArtifact := false,
+ // make-pom has a more specific publishArtifact setting already
+ // so needs specific override
+ makePom / publishArtifact := false,
+ // no docs to publish
+ packageDoc / publishArtifact := false,
+ publish / skip := true
+)
diff --git a/config/build.sbt b/config/build.sbt
deleted file mode 100644
index 7d3fbe218..000000000
--- a/config/build.sbt
+++ /dev/null
@@ -1,90 +0,0 @@
-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 Test := true
-fork in run := true
-fork in run in Test := true
-
-//env vars for tests
-envVars in Test ++= Map("testList.0" -> "0", "testList.1" -> "1")
-
-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")
diff --git a/config/checkstyle-config.xml b/config/checkstyle-config.xml
index b5b71d2b6..148167f31 100644
--- a/config/checkstyle-config.xml
+++ b/config/checkstyle-config.xml
@@ -1,8 +1,7 @@
-
+ "-//Checkstyle//DTD Check Configuration 1.3//EN"
+ "https://checkstyle.org/dtds/configuration_1_3.dtd">
diff --git a/config/checkstyle-suppressions.xml b/config/checkstyle-suppressions.xml
index 21fb8e51e..300751899 100644
--- a/config/checkstyle-suppressions.xml
+++ b/config/checkstyle-suppressions.xml
@@ -1,6 +1,7 @@
-
+
* You can find an example app and library on
+ * href="https://github.com/lightbend/config/tree/master/examples">on
* GitHub. Also be sure to read the package overview which
* describes the big picture as shown in those examples.
@@ -54,7 +56,7 @@
* 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
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">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
@@ -108,7 +110,7 @@
*
* Substitutions are the ${foo.bar} syntax in config
* files, described in the specification. Resolving substitutions replaces these references with real
* values.
*
@@ -187,7 +189,7 @@ public interface Config extends ConfigMergeable {
/**
* Returns a replacement config with all substitutions (the
* ${foo.bar} syntax, see the
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">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
@@ -668,7 +670,7 @@ public interface Config extends ConfigMergeable {
* 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
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
* spec.
*
* @param path
@@ -688,7 +690,7 @@ public interface Config extends ConfigMergeable {
* 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
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
* spec.
*
* @since 1.3.0
@@ -709,7 +711,7 @@ public interface Config extends ConfigMergeable {
* 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
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
* spec.
*
* @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)}
@@ -752,7 +754,7 @@ public interface Config extends ConfigMergeable {
* 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
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
* spec.
*
* @since 1.2.0
@@ -776,7 +778,7 @@ public interface Config extends ConfigMergeable {
* 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
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
* spec. This method never returns null.
*
* @since 1.3.0
@@ -793,6 +795,44 @@ public interface Config extends ConfigMergeable {
*/
Duration getDuration(String path);
+ /**
+ * Gets a value as a java.time.Period. If the value is
+ * already a number, then it's taken as days; if it's
+ * a string, it's parsed understanding units suffixes like
+ * "10d" or "5w" as documented in the the
+ * spec. This method never returns null.
+ *
+ * @since 1.3.0
+ *
+ * @param path
+ * path expression
+ * @return the period 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
+ */
+ Period getPeriod(String path);
+
+ /**
+ * Gets a value as a java.time.temporal.TemporalAmount.
+ * This method will first try get get the value as a java.time.Duration, and if unsuccessful,
+ * then as a java.time.Period.
+ * This means that values like "5m" will be parsed as 5 minutes rather than 5 months
+ * @param path path expression
+ * @return the temporal 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 TemporalAmount
+ */
+ TemporalAmount getTemporal(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
diff --git a/config/src/main/java/com/typesafe/config/ConfigException.java b/config/src/main/java/com/typesafe/config/ConfigException.java
index 58d541704..2e6d99ab7 100644
--- a/config/src/main/java/com/typesafe/config/ConfigException.java
+++ b/config/src/main/java/com/typesafe/config/ConfigException.java
@@ -58,22 +58,22 @@ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
ConfigImplUtil.writeOrigin(out, origin);
}
- private void readObject(java.io.ObjectInputStream in) throws IOException,
- ClassNotFoundException {
- in.defaultReadObject();
- ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
+ // For deserialization - uses reflection to set the final origin field on the object
+ private static void setOriginField(T hasOriginField, Class clazz,
+ ConfigOrigin origin) throws IOException {
// circumvent "final"
Field f;
try {
- f = ConfigException.class.getDeclaredField("origin");
+ f = clazz.getDeclaredField("origin");
} catch (NoSuchFieldException e) {
- throw new IOException("ConfigException has no origin field?", e);
+ throw new IOException(clazz.getSimpleName() + " has no origin field?", e);
} catch (SecurityException e) {
- throw new IOException("unable to fill out origin field in ConfigException", e);
+ throw new IOException("unable to fill out origin field in " +
+ clazz.getSimpleName(), e);
}
f.setAccessible(true);
try {
- f.set(this, origin);
+ f.set(hasOriginField, origin);
} catch (IllegalArgumentException e) {
throw new IOException("unable to set origin field", e);
} catch (IllegalAccessException e) {
@@ -81,6 +81,13 @@ private void readObject(java.io.ObjectInputStream in) throws IOException,
}
}
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
+ setOriginField(this, ConfigException.class, origin);
+ }
+
/**
* Exception indicating that the type of a value does not match the type you
* requested.
@@ -119,6 +126,10 @@ public Missing(String path, Throwable cause) {
cause);
}
+ public Missing(ConfigOrigin origin, String path) {
+ this(origin, "No configuration setting found for key '" + path + "'", null);
+ }
+
public Missing(String path) {
this(path, null);
}
@@ -127,9 +138,6 @@ protected Missing(ConfigOrigin origin, String message, Throwable cause) {
super(origin, message, cause);
}
- protected Missing(ConfigOrigin origin, String message) {
- this(origin, message, null);
- }
}
/**
@@ -276,13 +284,25 @@ public Parse(ConfigOrigin origin, String message) {
public static class UnresolvedSubstitution extends Parse {
private static final long serialVersionUID = 1L;
+ private final String detail;
+
public UnresolvedSubstitution(ConfigOrigin origin, String detail, Throwable cause) {
super(origin, "Could not resolve substitution to a value: " + detail, cause);
+ this.detail = detail;
}
public UnresolvedSubstitution(ConfigOrigin origin, String detail) {
this(origin, detail, null);
}
+
+ private UnresolvedSubstitution(UnresolvedSubstitution wrapped, ConfigOrigin origin, String message) {
+ super(origin, message, wrapped);
+ this.detail = wrapped.detail;
+ }
+
+ public UnresolvedSubstitution addExtraDetail(String extra) {
+ return new UnresolvedSubstitution(this, origin(), String.format(extra, detail));
+ }
}
/**
@@ -310,10 +330,10 @@ public NotResolved(String message) {
* {@link ConfigException.ValidationFailed} exception thrown from
* checkValid() includes a list of problems encountered.
*/
- public static class ValidationProblem {
+ public static class ValidationProblem implements Serializable {
final private String path;
- final private ConfigOrigin origin;
+ final private transient ConfigOrigin origin;
final private String problem;
public ValidationProblem(String path, ConfigOrigin origin, String problem) {
@@ -347,6 +367,20 @@ public String problem() {
return problem;
}
+ // We customize serialization because ConfigOrigin isn't
+ // serializable and we don't want it to be
+ 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);
+ setOriginField(this, ValidationProblem.class, origin);
+ }
+
@Override
public String toString() {
return "ValidationProblem(" + path + "," + origin + "," + problem + ")";
diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java
index 7938dc918..12f9aaec0 100644
--- a/config/src/main/java/com/typesafe/config/ConfigFactory.java
+++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java
@@ -28,7 +28,7 @@
* from a resource and nothing else.
*
*
You can find an example app and library on
+ * href="https://github.com/lightbend/config/tree/master/examples">on
* GitHub. Also be sure to read the package
* overview which describes the big picture as shown in those
@@ -36,6 +36,7 @@
*/
public final class ConfigFactory {
private static final String STRATEGY_PROPERTY_NAME = "config.strategy";
+ private static final String OVERRIDE_WITH_ENV_PROPERTY_NAME = "config.override_with_env_vars";
private ConfigFactory() {
}
@@ -210,7 +211,8 @@ public static Config load(Config config, ConfigResolveOptions resolveOptions) {
* @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))
+ return defaultOverrides(loader).withFallback(config)
+ .withFallback(ConfigImpl.defaultReferenceUnresolved(loader))
.resolve(resolveOptions);
}
@@ -367,6 +369,56 @@ public static Config defaultReference(ClassLoader loader) {
return ConfigImpl.defaultReference(loader);
}
+ /**
+ * 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.
+ *
+ *
+ * While the returned reference configuration is guaranteed to be
+ * resolvable (that is, there will be no substitutions that cannot be
+ * resolved), it is returned in an unresolved state for the purpose of
+ * allowing substitutions to be overridden by a config layer that falls
+ * back to this one.
+ *
+ *
+ * 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 unresolved default reference config for the context class
+ * loader
+ */
+ public static Config defaultReferenceUnresolved() {
+ return defaultReferenceUnresolved(checkedContextClassLoader("defaultReferenceUnresolved"));
+ }
+
+ /**
+ * Like {@link #defaultReferenceUnresolved()} 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 unresolved default reference config for this class loader
+ */
+ public static Config defaultReferenceUnresolved(ClassLoader loader) {
+ return ConfigImpl.defaultReferenceUnresolved(loader);
+ }
+
/**
* Obtains the default override configuration, which currently consists of
* system properties. The returned override configuration will already have
@@ -383,7 +435,11 @@ public static Config defaultReference(ClassLoader loader) {
* @return the default override configuration
*/
public static Config defaultOverrides() {
- return systemProperties();
+ if (getOverrideWithEnv()) {
+ return systemEnvironmentOverrides().withFallback(systemProperties());
+ } else {
+ return systemProperties();
+ }
}
/**
@@ -394,7 +450,7 @@ public static Config defaultOverrides() {
* @return the default override configuration
*/
public static Config defaultOverrides(ClassLoader loader) {
- return systemProperties();
+ return defaultOverrides();
}
/**
@@ -497,6 +553,7 @@ public static void invalidateCaches() {
// all caches
ConfigImpl.reloadSystemPropertiesConfig();
ConfigImpl.reloadEnvVariablesConfig();
+ ConfigImpl.reloadEnvVariablesOverridesConfig();
}
/**
@@ -549,6 +606,50 @@ public static Config systemProperties() {
return ConfigImpl.systemPropertiesAsConfig();
}
+ /**
+ * Gets a Config containing the system's environment variables
+ * used to override configuration keys.
+ * Environment variables taken in considerations are starting with
+ * {@code CONFIG_FORCE_}
+ *
+ *
+ * Environment variables are mangled in the following way after stripping the prefix "CONFIG_FORCE_":
+ *
+ *
+ *
Env Var
+ *
Config
+ *
+ *
+ *
_ [1 underscore]
+ *
. [dot]
+ *
+ *
+ *
__ [2 underscore]
+ *
- [dash]
+ *
+ *
+ *
___ [3 underscore]
+ *
_ [underscore]
+ *
+ *
+ *
+ *
+ * A variable like: {@code CONFIG_FORCE_a_b__c___d}
+ * is translated to a config key: {@code a.b-c_d}
+ *
+ *
+ * This method can return a global immutable singleton, so it's preferred
+ * over parsing system properties yourself.
+ *
+ * {@link #defaultOverrides} will include the system environment variables as
+ * overrides if `config.override_with_env_vars` is set to `true`.
+ *
+ * @return system environment variable overrides parsed into a Config
+ */
+ public static Config systemEnvironmentOverrides() {
+ return ConfigImpl.envVariablesOverridesAsConfig();
+ }
+
/**
* Gets a Config containing the system's environment variables.
* This method can return a global immutable singleton.
@@ -570,7 +671,7 @@ public static Config systemEnvironment() {
/**
* Converts a Java {@link java.util.Properties} object to a
* {@link ConfigObject} using the rules documented in the HOCON
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON
* spec. The keys in the Properties object are split on the
* period character '.' and treated as paths. The values will all end up as
* string values. If you have both "a=foo" and "a.b=bar" in your properties
@@ -1063,4 +1164,10 @@ private static ConfigLoadingStrategy getConfigLoadingStrategy() {
return new DefaultConfigLoadingStrategy();
}
}
+
+ private static Boolean getOverrideWithEnv() {
+ String overrideWithEnv = System.getProperties().getProperty(OVERRIDE_WITH_ENV_PROPERTY_NAME);
+
+ return Boolean.parseBoolean(overrideWithEnv);
+ }
}
diff --git a/config/src/main/java/com/typesafe/config/ConfigMergeable.java b/config/src/main/java/com/typesafe/config/ConfigMergeable.java
index 7af36f7bf..58b2663e8 100644
--- a/config/src/main/java/com/typesafe/config/ConfigMergeable.java
+++ b/config/src/main/java/com/typesafe/config/ConfigMergeable.java
@@ -27,7 +27,7 @@ public interface ConfigMergeable {
*
*
* The semantics of merging are described in the spec
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">spec
* for HOCON. Merging typically occurs when either the same object is
* created twice in the same file, or two config files are both loaded. For
* example:
diff --git a/config/src/main/java/com/typesafe/config/ConfigParseOptions.java b/config/src/main/java/com/typesafe/config/ConfigParseOptions.java
index fcfb4d69a..9dc7a121a 100644
--- a/config/src/main/java/com/typesafe/config/ConfigParseOptions.java
+++ b/config/src/main/java/com/typesafe/config/ConfigParseOptions.java
@@ -4,6 +4,8 @@
package com.typesafe.config;
+import com.typesafe.config.impl.ConfigImplUtil;
+
/**
* A set of options related to parsing.
*
@@ -62,6 +64,18 @@ public ConfigParseOptions setSyntax(ConfigSyntax syntax) {
this.includer, this.classLoader);
}
+ /**
+ * Set the file format. If set to null, assume {@link ConfigSyntax#CONF}.
+ *
+ * @param filename
+ * a configuration file name
+ * @return options with the syntax set
+ */
+ public ConfigParseOptions setSyntaxFromFilename(String filename) {
+ ConfigSyntax syntax = ConfigImplUtil.syntaxFromExtension(filename);
+ return setSyntax(syntax);
+ }
+
/**
* Gets the current syntax option, which may be null for "any".
* @return the current syntax or null
diff --git a/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java b/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java
index 2b7140416..526da1eff 100644
--- a/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java
+++ b/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java
@@ -6,7 +6,7 @@
/**
* A set of options related to resolving substitutions. Substitutions use the
* ${foo.bar} syntax and are documented in the HOCON
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON
* spec.
*
* Typically this class would be used with the method
@@ -29,10 +29,13 @@
public final class ConfigResolveOptions {
private final boolean useSystemEnvironment;
private final boolean allowUnresolved;
+ private final ConfigResolver resolver;
- private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved) {
+ private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved,
+ ConfigResolver resolver) {
this.useSystemEnvironment = useSystemEnvironment;
this.allowUnresolved = allowUnresolved;
+ this.resolver = resolver;
}
/**
@@ -42,7 +45,7 @@ private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolv
* @return the default resolve options
*/
public static ConfigResolveOptions defaults() {
- return new ConfigResolveOptions(true, false);
+ return new ConfigResolveOptions(true, false, NULL_RESOLVER);
}
/**
@@ -64,7 +67,7 @@ public static ConfigResolveOptions noSystem() {
* @return options with requested setting for use of environment variables
*/
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
- return new ConfigResolveOptions(value, allowUnresolved);
+ return new ConfigResolveOptions(value, allowUnresolved, resolver);
}
/**
@@ -91,7 +94,55 @@ public boolean getUseSystemEnvironment() {
* @since 1.2.0
*/
public ConfigResolveOptions setAllowUnresolved(boolean value) {
- return new ConfigResolveOptions(useSystemEnvironment, value);
+ return new ConfigResolveOptions(useSystemEnvironment, value, resolver);
+ }
+
+ /**
+ * Returns options where the given resolver used as a fallback if a
+ * reference cannot be otherwise resolved. This resolver will only be called
+ * after resolution has failed to substitute with a value from within the
+ * config itself and with any other resolvers that have been appended before
+ * this one. Multiple resolvers can be added using,
+ *
+ *
-An API for loading and using configuration files, see the project site
+An API for loading and using configuration files, see the project site
for more information.
Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use
it with methods in the {@link com.typesafe.config.Config} interface. Configuration may be in the form of JSON files,
-Java properties, or HOCON files; you may also
+Java properties, or HOCON files; you may also
build your own configuration in code or from your own file formats.
@@ -26,8 +26,8 @@
If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()}
there's no need to pass a configuration to your libraries
and frameworks, as long as they all default to this same default, which they should.
- Example application code:Java and Scala.
- Showing a couple of more special-purpose features, a more complex example:Java and Scala.
+ Example application code:Java and Scala.
+ Showing a couple of more special-purpose features, a more complex example:Java and Scala.
@@ -36,21 +36,21 @@
call {@link com.typesafe.config.ConfigFactory#load()}
to get the default one. Typically a library might offer two constructors, one with a Config parameter
and one which uses {@link com.typesafe.config.ConfigFactory#load()}.
- Example library code:Java and Scala.
+ Example library code:Java and Scala.
If you want to use .conf files in addition to .json and .properties,
- see the README for some short examples
- and the full HOCON spec for the long version.
+ see the README for some short examples
+ and the full HOCON spec for the long version.
diff --git a/config/src/main/java/com/typesafe/config/parser/ConfigNode.java b/config/src/main/java/com/typesafe/config/parser/ConfigNode.java
index 43ac3f51b..e9954e9a7 100644
--- a/config/src/main/java/com/typesafe/config/parser/ConfigNode.java
+++ b/config/src/main/java/com/typesafe/config/parser/ConfigNode.java
@@ -10,8 +10,8 @@
* Note: at present there is no way to obtain an instance of this interface, so
* please ignore it. A future release will make syntax tree nodes available in
* the public API. If you are interested in working on it, please see: https://github.com/typesafehub/config/issues/300
+ * href="https://github.com/lightbend/config/issues/300"
+ * >https://github.com/lightbend/config/issues/300
*
*
* Because this object is immutable, it is safe to use from multiple threads and
diff --git a/config/src/main/java/com/typesafe/config/parser/package.html b/config/src/main/java/com/typesafe/config/parser/package.html
index ef099cc4d..b0e7b3cea 100644
--- a/config/src/main/java/com/typesafe/config/parser/package.html
+++ b/config/src/main/java/com/typesafe/config/parser/package.html
@@ -14,7 +14,7 @@
the com.typesafe.config package instead. You would use the raw
parser if you're doing something like reading, modifying, and re-saving a config
file. For info on the main config API this parser is a part of,
-see the project site.
+see the project site.
diff --git a/config/src/test/java/beanconfig/ObjectsConfig.java b/config/src/test/java/beanconfig/ObjectsConfig.java
index f3009c96d..11d040f50 100644
--- a/config/src/test/java/beanconfig/ObjectsConfig.java
+++ b/config/src/test/java/beanconfig/ObjectsConfig.java
@@ -8,6 +8,7 @@ public static class ValueObject {
@Optional
private String optionalValue;
private String mandatoryValue;
+ private String Default;
public String getMandatoryValue() {
return mandatoryValue;
@@ -24,6 +25,15 @@ public String getOptionalValue() {
public void setOptionalValue(String optionalValue) {
this.optionalValue = optionalValue;
}
+
+ @Optional
+ public String getDefault() {
+ return Default;
+ }
+
+ public void setDefault(String Default) {
+ this.Default = Default;
+ }
}
private ValueObject valueObject;
diff --git a/config/src/test/java/beanconfig/SetsConfig.java b/config/src/test/java/beanconfig/SetsConfig.java
new file mode 100644
index 000000000..b81540df9
--- /dev/null
+++ b/config/src/test/java/beanconfig/SetsConfig.java
@@ -0,0 +1,139 @@
+package beanconfig;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigMemorySize;
+import com.typesafe.config.ConfigObject;
+import com.typesafe.config.ConfigValue;
+
+import java.time.Duration;
+import java.util.Set;
+
+public class SetsConfig {
+
+ Set empty;
+ Set ofInt;
+ Set ofString;
+ Set ofDouble;
+ Set ofLong;
+ Set