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)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
 ## 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.
     <dependency>
         <groupId>com.typesafe</groupId>
         <artifactId>config</artifactId>
-        <version>1.3.1</version>
+        <version>1.4.0</version>
     </dependency>
 
 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 <a
-href="#note-about-resolving-substitutions-in-referenceconf-and-applicationconf">this
-earlier section</a>; 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 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE module PUBLIC
-    "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
-    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
-
+    "-//Checkstyle//DTD Check Configuration 1.3//EN"
+    "https://checkstyle.org/dtds/configuration_1_3.dtd">
 <module name="Checker">
   <module name="SuppressionFilter">
     <property name="file" value="config/checkstyle-suppressions.xml"/>
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 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
-    "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+<!DOCTYPE suppressions PUBLIC
+    "-//Checkstyle//DTD Suppressions 1.1//EN"
+    "https://checkstyle.org/dtds/suppressions_1_1.dtd">
 <suppressions>
   <!-- don't care about javadoc coverage of methods in impl -->
   <suppress checks="JavadocMethod"
diff --git a/config/src/main/java/com/typesafe/config/Config.java b/config/src/main/java/com/typesafe/config/Config.java
index 0097eed6a..d9aea4c4b 100644
--- a/config/src/main/java/com/typesafe/config/Config.java
+++ b/config/src/main/java/com/typesafe/config/Config.java
@@ -4,6 +4,8 @@
 package com.typesafe.config;
 
 import java.time.Duration;
+import java.time.Period;
+import java.time.temporal.TemporalAmount;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -34,7 +36,7 @@
  * 
  * <p>
  * You can find an example app and library <a
- * href="https://github.com/typesafehub/config/tree/master/examples">on
+ * href="https://github.com/lightbend/config/tree/master/examples">on
  * GitHub</a>. Also be sure to read the <a
  * href="package-summary.html#package_description">package overview</a> 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 <a
- * href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec for
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">spec for
  * Human-Optimized Config Object Notation</a>. 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 @@
  * <p>
  * <em>Substitutions</em> are the <code>${foo.bar}</code> syntax in config
  * files, described in the <a href=
- * "https://github.com/typesafehub/config/blob/master/HOCON.md#substitutions"
+ * "https://github.com/lightbend/config/blob/master/HOCON.md#substitutions"
  * >specification</a>. 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
      * <code>${foo.bar}</code> syntax, see <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
      * spec</a>) resolved. Substitutions are looked up using this
      * <code>Config</code> as the root object, that is, a substitution
      * <code>${foo.bar}</code> 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 <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
      * spec</a>.
      *
      * @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 <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
      * spec</a>.
      *
      * @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 <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
      * spec</a>.
      *
      * @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 <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
      * spec</a>.
      * 
      * @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 <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
      * spec</a>. 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 <a
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">the
+     * spec</a>. 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<ConfigValue>}. 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 <T> void setOriginField(T hasOriginField, Class<T> 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
      * <code>checkValid()</code> 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.
  *
  * <p> You can find an example app and library <a
- * href="https://github.com/typesafehub/config/tree/master/examples">on
+ * href="https://github.com/lightbend/config/tree/master/examples">on
  * GitHub</a>.  Also be sure to read the <a
  * href="package-summary.html#package_description">package
  * overview</a> 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.
+     *
+     * <p>
+     * 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.
+     *
+     * <p>
+     * Libraries and frameworks should ship with a "reference.conf" in their
+     * jar.
+     *
+     * <p>
+     * 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.
+     *
+     * <p>
+     * The {@link #load()} methods merge this configuration for you
+     * automatically.
+     *
+     * <p>
+     * Future versions may look for reference configuration in more places. It
+     * is not guaranteed that this method <em>only</em> looks at
+     * "reference.conf".
+     *
+     * @return the 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 <code>Config</code> containing the system's environment variables
+     * used to override configuration keys.
+     * Environment variables taken in considerations are starting with
+     * {@code CONFIG_FORCE_}
+     * 
+     * <p>
+     * Environment variables are mangled in the following way after stripping the prefix "CONFIG_FORCE_":
+     * <table border="1">
+     * <tr>
+     *     <th bgcolor="silver">Env Var</th>
+     *     <th bgcolor="silver">Config</th>
+     * </tr>
+     * <tr>
+     *     <td>_&nbsp;&nbsp;&nbsp;[1 underscore]</td>
+     *     <td>. [dot]</td>
+     * </tr>
+     * <tr>
+     *     <td>__&nbsp;&nbsp;[2 underscore]</td>
+     *     <td>- [dash]</td>
+     *  </tr>
+     * <tr>
+     *     <td>___&nbsp;[3 underscore]</td>
+     *     <td>_ [underscore]</td>
+     * </tr>
+     * </table>
+     * 
+     * <p>
+     * A variable like: {@code CONFIG_FORCE_a_b__c___d}
+     * is translated to a config key: {@code a.b-c_d}
+     * 
+     * <p>
+     * This method can return a global immutable singleton, so it's preferred
+     * over parsing system properties yourself.
+     * <p>
+     * {@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 <code>Config</code>
+     */
+    public static Config systemEnvironmentOverrides() {
+        return ConfigImpl.envVariablesOverridesAsConfig();
+    }
+
     /**
      * Gets a <code>Config</code> 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 <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON
      * spec</a>. The keys in the <code>Properties</code> object are split on the
      * period character '.' and treated as paths. The values will all end up as
      * string values. If you have both "a=foo" and "a.b=bar" in your properties
@@ -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 {
      * 
      * <p>
      * The semantics of merging are described in the <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md">spec
      * for HOCON</a>. 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
  * <code>${foo.bar}</code> syntax and are documented in the <a
- * href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON</a>
  * spec.
  * <p>
  * 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,
+     *
+     *  <pre>
+     *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
+     *         .appendResolver(primary)
+     *         .appendResolver(secondary)
+     *         .appendResolver(tertiary);
+     * </pre>
+     *
+     * With this config unresolved references will first be resolved with the
+     * primary resolver, if that fails then the secondary, and finally if that
+     * also fails the tertiary.
+     *
+     * If all fallbacks fail to return a substitution "allow unresolved"
+     * determines whether resolution fails or continues.
+     *`
+     * @param value the resolver to fall back to
+     * @return options that use the given resolver as a fallback
+     * @since 1.3.2
+     */
+    public ConfigResolveOptions appendResolver(ConfigResolver value) {
+        if (value == null) {
+            throw new ConfigException.BugOrBroken("null resolver passed to appendResolver");
+        } else if (value == this.resolver) {
+            return this;
+        } else {
+            return new ConfigResolveOptions(useSystemEnvironment, allowUnresolved,
+                    this.resolver.withFallback(value));
+        }
+    }
+
+    /**
+     * Returns the resolver to use as a fallback if a substitution cannot be
+     * otherwise resolved. Never returns null. This method is mostly used by the
+     * config lib internally, not by applications.
+     *
+     * @return the non-null fallback resolver
+     * @since 1.3.2
+     */
+    public ConfigResolver getResolver() {
+        return this.resolver;
     }
 
     /**
@@ -104,4 +155,22 @@ public ConfigResolveOptions setAllowUnresolved(boolean value) {
     public boolean getAllowUnresolved() {
         return allowUnresolved;
     }
+
+    /**
+     * Singleton resolver that never resolves paths.
+     */
+    private static final ConfigResolver NULL_RESOLVER = new ConfigResolver() {
+
+        @Override
+        public ConfigValue lookup(String path) {
+            return null;
+        }
+
+        @Override
+        public ConfigResolver withFallback(ConfigResolver fallback) {
+            return fallback;
+        }
+
+    };
+
 }
diff --git a/config/src/main/java/com/typesafe/config/ConfigResolver.java b/config/src/main/java/com/typesafe/config/ConfigResolver.java
new file mode 100644
index 000000000..a380a04f4
--- /dev/null
+++ b/config/src/main/java/com/typesafe/config/ConfigResolver.java
@@ -0,0 +1,38 @@
+package com.typesafe.config;
+
+/**
+ * Implement this interface and provide an instance to
+ * {@link ConfigResolveOptions#appendResolver ConfigResolveOptions.appendResolver()}
+ * to provide custom behavior when unresolved substitutions are encountered
+ * during resolution.
+ * @since 1.3.2
+ */
+public interface ConfigResolver {
+
+    /**
+     * Returns the value to substitute for the given unresolved path. To get the
+     * components of the path use {@link ConfigUtil#splitPath(String)}. If a
+     * non-null value is returned that value will be substituted, otherwise
+     * resolution will continue to consider the substitution as still
+     * unresolved.
+     *
+     * @param path the unresolved path
+     * @return the value to use as a substitution or null
+     */
+    public ConfigValue lookup(String path);
+
+    /**
+     * Returns a new resolver that falls back to the given resolver if this
+     * one doesn't provide a substitution itself.
+     *
+     * 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 resolver
+     */
+    public ConfigResolver withFallback(ConfigResolver fallback);
+
+}
diff --git a/config/src/main/java/com/typesafe/config/ConfigSyntax.java b/config/src/main/java/com/typesafe/config/ConfigSyntax.java
index ed560296c..749387d7c 100644
--- a/config/src/main/java/com/typesafe/config/ConfigSyntax.java
+++ b/config/src/main/java/com/typesafe/config/ConfigSyntax.java
@@ -5,7 +5,7 @@
 
 /**
  * The syntax of a character stream (<a href="http://json.org">JSON</a>, <a
- * href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
+ * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON</a>
  * aka ".conf", or <a href=
  * "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
  * >Java properties</a>).
@@ -21,7 +21,7 @@ public enum ConfigSyntax {
     JSON,
     /**
      * The JSON-superset <a
-     * href="https://github.com/typesafehub/config/blob/master/HOCON.md"
+     * href="https://github.com/lightbend/config/blob/master/HOCON.md"
      * >HOCON</a> format. Associated with the <code>.conf</code> file extension
      * and <code>application/hocon</code> Content-Type.
      */
diff --git a/config/src/main/java/com/typesafe/config/Optional.java b/config/src/main/java/com/typesafe/config/Optional.java
index 4645ed5fb..5498b8f41 100644
--- a/config/src/main/java/com/typesafe/config/Optional.java
+++ b/config/src/main/java/com/typesafe/config/Optional.java
@@ -1,6 +1,7 @@
 package com.typesafe.config;
 
 import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -8,6 +9,7 @@
  * Allows an config property to be {@code null}.
  */
 @Documented
+@Inherited
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Optional {
 
diff --git a/config/src/main/java/com/typesafe/config/impl/BadMap.java b/config/src/main/java/com/typesafe/config/impl/BadMap.java
new file mode 100644
index 000000000..04b375367
--- /dev/null
+++ b/config/src/main/java/com/typesafe/config/impl/BadMap.java
@@ -0,0 +1,137 @@
+package com.typesafe.config.impl;
+
+/**
+ * A terrible Map that isn't as expensive as HashMap to copy and
+ * add one item to. Please write something real if you see this
+ * and get cranky.
+ */
+final class BadMap<K,V> {
+    final static class Entry {
+        final int hash;
+        final Object key;
+        final Object value;
+        final Entry next;
+
+        Entry(int hash, Object k, Object v, Entry next) {
+            this.hash = hash;
+            this.key = k;
+            this.value = v;
+            this.next = next;
+        }
+
+        Object find(Object k) {
+            if (key.equals(k))
+                return value;
+            else if (next != null)
+                return next.find(k);
+            else
+                return null;
+        }
+    }
+
+    private final int size;
+    private final Entry[] entries;
+
+    private final static Entry[] emptyEntries = {};
+
+    BadMap() {
+        this(0, emptyEntries);
+    }
+
+    private BadMap(int size, Entry[] entries) {
+        this.size = size;
+        this.entries = entries;
+    }
+
+    BadMap<K,V> copyingPut(K k, V v) {
+        int newSize = size + 1;
+        Entry[] newEntries;
+        if (newSize > entries.length) {
+            // nextPrime doesn't always return a prime larger than
+            // we passed in, so this block may not actually change
+            // the entries size. the "-1" is to ensure we use
+            // array length 2 when going from 0 to 1.
+            newEntries = new Entry[nextPrime((newSize * 2) - 1)];
+        } else {
+            newEntries = new Entry[entries.length];
+        }
+
+        if (newEntries.length == entries.length) {
+            System.arraycopy(entries, 0, newEntries, 0, entries.length);
+        } else {
+            rehash(entries, newEntries);
+        }
+
+        int hash = Math.abs(k.hashCode());
+        store(newEntries, hash, k, v);
+        return new BadMap<>(newSize, newEntries);
+    }
+
+    private static <K,V> void store(Entry[] entries, int hash, K k, V v) {
+        int i = hash % entries.length;
+        Entry old = entries[i];  // old may be null
+        entries[i] = new Entry(hash, k, v, old);
+    }
+
+    private static void store(Entry[] entries, Entry e) {
+        int i = e.hash % entries.length;
+        Entry old = entries[i]; // old may be null
+        if (old == null && e.next == null) {
+            // share the entry since it has no "next"
+            entries[i] = e;
+        } else {
+            // bah, have to copy it
+            entries[i] = new Entry(e.hash, e.key, e.value, old);
+        }
+    }
+
+    private static void rehash(Entry[] src, Entry[] dest) {
+        for (Entry entry : src) {
+            // have to store each "next" element individually; they may belong in different indices
+            while (entry != null) {
+                store(dest, entry);
+                entry = entry.next;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    V get(K k) {
+        if (entries.length == 0) {
+            return null;
+        } else {
+            int hash = Math.abs(k.hashCode());
+            int i = hash % entries.length;
+            Entry e = entries[i];
+            if (e == null)
+                return null;
+            else
+                return (V) e.find(k);
+        }
+    }
+
+
+    private final static int[] primes = {
+        /* Skip some early ones that are close together */
+        2, /* 3, */ 5, /* 7, */ 11, /* 13, */ 17, /* 19, */ 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+        73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
+        179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
+        283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
+        419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
+        547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
+        661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
+        811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
+        947, 953, 967, 971, 977, 983, 991, 997, 1009,
+        /* now we start skipping some, this is arbitrary */
+        2053, 3079, 4057, 7103, 10949, 16069, 32609, 65867, 104729
+    };
+
+    private static int nextPrime(int i) {
+        for (int p : primes) {
+            if (p > i)
+                return p;
+        }
+        /* oh well */
+        return primes[primes.length - 1];
+    }
+}
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java b/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java
index 69da879b4..902311359 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java
@@ -11,9 +11,11 @@
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.time.Duration;
+import java.util.Set;
 
 import com.typesafe.config.Config;
 import com.typesafe.config.ConfigObject;
@@ -160,6 +162,8 @@ private static Object getValue(Class<?> beanClass, Type parameterType, Class<?>
             return config.getAnyRef(configPropName);
         } else if (parameterClass == List.class) {
             return getListValue(beanClass, parameterType, parameterClass, config, configPropName);
+        } else if (parameterClass == Set.class) {
+            return getSetValue(beanClass, parameterType, parameterClass, config, configPropName);
         } else if (parameterClass == Map.class) {
             // we could do better here, but right now we don't.
             Type[] typeArgs = ((ParameterizedType)parameterType).getActualTypeArguments();
@@ -186,6 +190,10 @@ private static Object getValue(Class<?> beanClass, Type parameterType, Class<?>
         }
     }
 
+    private static Object getSetValue(Class<?> beanClass, Type parameterType, Class<?> parameterClass, Config config, String configPropName) {
+        return new HashSet((List) getListValue(beanClass, parameterType, parameterClass, config, configPropName));
+    }
+
     private static Object getListValue(Class<?> beanClass, Type parameterType, Class<?> parameterClass, Config config, String configPropName) {
         Type elementType = ((ParameterizedType)parameterType).getActualTypeArguments()[0];
 
@@ -277,7 +285,10 @@ private static boolean hasAtLeastOneBeanProperty(Class<?> clazz) {
 
     private static boolean isOptionalProperty(Class beanClass, PropertyDescriptor beanProp) {
         Field field = getField(beanClass, beanProp.getName());
-        return field != null && (field.getAnnotationsByType(Optional.class).length > 0);
+        return field != null
+                ? field.getAnnotationsByType(Optional.class).length > 0
+                        || beanProp.getReadMethod().getAnnotationsByType(Optional.class).length > 0
+                : beanProp.getReadMethod().getAnnotationsByType(Optional.class).length > 0;
     }
 
     private static Field getField(Class beanClass, String fieldName) {
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigDocumentParser.java b/config/src/main/java/com/typesafe/config/impl/ConfigDocumentParser.java
index 235608a3e..29ffc70a1 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigDocumentParser.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigDocumentParser.java
@@ -261,7 +261,8 @@ private AbstractConfigNodeValue parseValue(Token t) {
         private ConfigNodePath parseKey(Token token) {
             if (flavor == ConfigSyntax.JSON) {
                 if (Tokens.isValueWithType(token, ConfigValueType.STRING)) {
-                    return PathParser.parsePathNodeExpression(Collections.singletonList(token).iterator(), null);
+                    return PathParser.parsePathNodeExpression(Collections.singletonList(token).iterator(),
+                                                              baseOrigin.withLineNumber(lineNumber));
                 } else {
                     throw parseError("Expecting close brace } or a field name here, got "
                             + token);
@@ -279,7 +280,8 @@ private ConfigNodePath parseKey(Token token) {
                 }
 
                 putBack(t); // put back the token we ended with
-                return PathParser.parsePathNodeExpression(expression.iterator(), null);
+                return PathParser.parsePathNodeExpression(expression.iterator(),
+                                                          baseOrigin.withLineNumber(lineNumber));
             }
         }
 
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
index 9cf49913b..f14c12af6 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
@@ -32,6 +32,7 @@
  * For use only by the {@link com.typesafe.config} package.
  */
 public class ConfigImpl {
+    private static final String ENV_VAR_OVERRIDE_PREFIX = "CONFIG_FORCE_";
 
     private static class LoaderCache {
         private Config currentSystemProperties;
@@ -301,7 +302,13 @@ private static Properties getSystemProperties() {
         final Properties systemProperties = System.getProperties();
         final Properties systemPropertiesCopy = new Properties();
         synchronized (systemProperties) {
-            systemPropertiesCopy.putAll(systemProperties);
+            for (Map.Entry<Object, Object> entry: systemProperties.entrySet()) {
+                // Java 11 introduces 'java.version.date', but we don't want that to
+                // overwrite 'java.version'
+                if (!entry.getKey().toString().startsWith("java.version.")) {
+                    systemPropertiesCopy.put(entry.getKey(), entry.getValue());
+                }
+            }
         }
         return systemPropertiesCopy;
     }
@@ -360,19 +367,82 @@ public static void reloadEnvVariablesConfig() {
         EnvVariablesHolder.envVariables = loadEnvVariables();
     }
 
+
+
+    private static AbstractConfigObject loadEnvVariablesOverrides() {
+        Map<String, String> env = new HashMap(System.getenv());
+        Map<String, String> result = new HashMap(System.getenv());
+
+        for (String key : env.keySet()) {
+            if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) {
+                result.put(ConfigImplUtil.envVariableAsProperty(key, ENV_VAR_OVERRIDE_PREFIX), env.get(key));
+            }
+        }
+
+        return PropertiesParser.fromStringMap(newSimpleOrigin("env variables overrides"), result);
+    }
+
+    private static class EnvVariablesOverridesHolder {
+        static volatile AbstractConfigObject envVariables = loadEnvVariablesOverrides();
+    }
+
+    static AbstractConfigObject envVariablesOverridesAsConfigObject() {
+        try {
+            return EnvVariablesOverridesHolder.envVariables;
+        } catch (ExceptionInInitializerError e) {
+            throw ConfigImplUtil.extractInitializerError(e);
+        }
+    }
+
+    public static Config envVariablesOverridesAsConfig() {
+        return envVariablesOverridesAsConfigObject().toConfig();
+    }
+
+    public static void reloadEnvVariablesOverridesConfig() {
+        // ConfigFactory.invalidateCaches() relies on this having the side
+        // effect that it drops all caches
+        EnvVariablesOverridesHolder.envVariables = loadEnvVariablesOverrides();
+    }
+
     public static Config defaultReference(final ClassLoader loader) {
         return computeCachedConfig(loader, "defaultReference", new Callable<Config>() {
             @Override
             public Config call() {
-                Config unresolvedResources = Parseable
-                        .newResources("reference.conf",
-                                ConfigParseOptions.defaults().setClassLoader(loader))
-                        .parse().toConfig();
+                Config unresolvedResources = unresolvedReference(loader);
                 return systemPropertiesAsConfig().withFallback(unresolvedResources).resolve();
             }
         });
     }
 
+    private static Config unresolvedReference(final ClassLoader loader) {
+        return computeCachedConfig(loader, "unresolvedReference", new Callable<Config>() {
+            @Override
+            public Config call() {
+                return Parseable.newResources("reference.conf",
+                        ConfigParseOptions.defaults().setClassLoader(loader))
+                    .parse().toConfig();
+            }
+        });
+    }
+
+    /**
+     * This returns the unresolved reference configuration, but before doing so,
+     * it verifies that the reference configuration resolves, to ensure that it
+     * is self contained and doesn't depend on any higher level configuration
+     * files.
+     */
+    public static Config defaultReferenceUnresolved(final ClassLoader loader) {
+        // First, verify that `reference.conf` resolves by itself.
+        try {
+            defaultReference(loader);
+        } catch (ConfigException.UnresolvedSubstitution e) {
+            throw e.addExtraDetail("Could not resolve substitution in reference.conf to a value: %s. All reference.conf files are required to be fully, independently resolvable, and should not require the presence of values for substitutions from further up the hierarchy.");
+        }
+        // Now load the unresolved version
+        return unresolvedReference(loader);
+    }
+
+
     private static class DebugHolder {
         private static String LOADS = "loads";
         private static String SUBSTITUTIONS = "substitutions";
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java b/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java
index 279f5ec84..1a8ffc1db 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java
@@ -15,6 +15,7 @@
 
 import com.typesafe.config.ConfigException;
 import com.typesafe.config.ConfigOrigin;
+import com.typesafe.config.ConfigSyntax;
 
 /**
  * Internal implementation detail, not ABI stable, do not touch.
@@ -233,4 +234,69 @@ static String toCamelCase(String originalName) {
         }
         return nameBuilder.toString();
     }
+
+    private static char underscoreMappings(int num) {
+        // Rationale on name mangling:
+        //
+        // Most shells (e.g. bash, sh, etc.) doesn't support any character other
+        // than alphanumeric and `_` in environment variables names.
+        // In HOCON the default separator is `.` so it is directly translated to a
+        // single `_` for convenience; `-` and `_` are less often present in config
+        // keys but they have to be representable and the only possible mapping is
+        // `_` repeated.
+        switch (num) {
+            case 1: return '.';
+            case 2: return '-';
+            case 3: return '_';
+            default: return 0;
+        }
+    }
+
+    static String envVariableAsProperty(String variable, String prefix) throws ConfigException {
+        StringBuilder builder = new StringBuilder();
+
+        String strippedPrefix = variable.substring(prefix.length(), variable.length());
+
+        int underscores = 0;
+        for (char c : strippedPrefix.toCharArray()) {
+            if (c == '_') {
+                underscores++;
+            } else {
+                if (underscores > 0  && underscores < 4) {
+                    builder.append(underscoreMappings(underscores));
+                } else if (underscores > 3) {
+                    throw new ConfigException.BadPath(variable, "Environment variable contains an un-mapped number of underscores.");
+                }
+                underscores = 0;
+                builder.append(c);
+            }
+        }
+
+        if (underscores > 0  && underscores < 4) {
+            builder.append(underscoreMappings(underscores));
+        } else if (underscores > 3) {
+            throw new ConfigException.BadPath(variable, "Environment variable contains an un-mapped number of underscores.");
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * Guess configuration syntax from given filename.
+     *
+     * @param filename configuration filename
+     * @return configuration syntax if a match is found. Otherwise, null.
+     */
+    public static ConfigSyntax syntaxFromExtension(String filename) {
+        if (filename == null)
+            return null;
+        else if (filename.endsWith(".json"))
+            return ConfigSyntax.JSON;
+        else if (filename.endsWith(".conf"))
+            return ConfigSyntax.CONF;
+        else if (filename.endsWith(".properties"))
+            return ConfigSyntax.PROPERTIES;
+        else
+            return null;
+    }
 }
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigParser.java b/config/src/main/java/com/typesafe/config/impl/ConfigParser.java
index b2ce04553..1e562ace2 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigParser.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigParser.java
@@ -59,7 +59,7 @@ private AbstractConfigValue parseConcatenation(ConfigNodeConcatenation n) {
             if (flavor == ConfigSyntax.JSON)
                 throw new ConfigException.BugOrBroken("Found a concatenation node in JSON");
 
-            List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
+            List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>(n.children().size());
 
             for (AbstractConfigNode node : n.children()) {
                 AbstractConfigValue v = null;
@@ -192,7 +192,7 @@ private void parseInclude(Map<String, AbstractConfigValue> values, ConfigNodeInc
 
             // we really should make this work, but for now throwing an
             // exception is better than producing an incorrect result.
-            // See https://github.com/typesafehub/config/issues/160
+            // See https://github.com/lightbend/config/issues/160
             if (arrayCount > 0 && obj.resolveStatus() != ResolveStatus.RESOLVED)
                 throw parseError("Due to current limitations of the config parser, when an include statement is nested inside a list value, "
                         + "${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or "
@@ -247,7 +247,7 @@ private AbstractConfigObject parseObject(ConfigNodeObject n) {
                         // we really should make this work, but for now throwing
                         // an exception is better than producing an incorrect
                         // result. See
-                        // https://github.com/typesafehub/config/issues/160
+                        // https://github.com/lightbend/config/issues/160
                         if (arrayCount > 0)
                             throw parseError("Due to current limitations of the config parser, += does not work nested inside a list. "
                                     + "+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. "
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigReference.java b/config/src/main/java/com/typesafe/config/impl/ConfigReference.java
index 8d3c7c0c0..077503d40 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigReference.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigReference.java
@@ -6,6 +6,8 @@
 import com.typesafe.config.ConfigException;
 import com.typesafe.config.ConfigOrigin;
 import com.typesafe.config.ConfigRenderOptions;
+import com.typesafe.config.ConfigResolveOptions;
+import com.typesafe.config.ConfigValue;
 import com.typesafe.config.ConfigValueType;
 
 /**
@@ -88,7 +90,8 @@ ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ResolveContext
                 v = result.value;
                 newContext = result.context;
             } else {
-                v = null;
+                ConfigValue fallback = context.options().getResolver().lookup(expr.path().render());
+                v = (AbstractConfigValue) fallback;
             }
         } catch (NotPossibleToResolve e) {
             if (ConfigImpl.traceSubstitutionsEnabled())
diff --git a/config/src/main/java/com/typesafe/config/impl/Parseable.java b/config/src/main/java/com/typesafe/config/impl/Parseable.java
index 9f66af195..ad5146301 100644
--- a/config/src/main/java/com/typesafe/config/impl/Parseable.java
+++ b/config/src/main/java/com/typesafe/config/impl/Parseable.java
@@ -180,6 +180,8 @@ final private AbstractConfigValue parseValue(ConfigOrigin origin,
             return rawParseValue(origin, finalOptions);
         } catch (IOException e) {
             if (finalOptions.getAllowMissing()) {
+                trace(e.getMessage() + ". Allowing Missing File, this can be turned off by setting" +
+                        " ConfigParseOptions.allowMissing = false");
                 return SimpleConfigObject.emptyMissing(origin);
             } else {
                 trace("exception loading " + origin.description() + ": " + e.getClass().getName()
@@ -324,17 +326,6 @@ public String toString() {
         return getClass().getSimpleName();
     }
 
-    private static ConfigSyntax syntaxFromExtension(String name) {
-        if (name.endsWith(".json"))
-            return ConfigSyntax.JSON;
-        else if (name.endsWith(".conf"))
-            return ConfigSyntax.CONF;
-        else if (name.endsWith(".properties"))
-            return ConfigSyntax.PROPERTIES;
-        else
-            return null;
-    }
-
     private static Reader readerFromStream(InputStream input) {
         return readerFromStream(input, "UTF-8");
     }
@@ -572,7 +563,7 @@ protected Reader reader(ConfigParseOptions options) throws IOException {
 
         @Override
         ConfigSyntax guessSyntax() {
-            return syntaxFromExtension(input.getPath());
+            return ConfigImplUtil.syntaxFromExtension(input.getPath());
         }
 
         @Override
@@ -641,7 +632,7 @@ protected Reader reader() throws IOException {
 
         @Override
         ConfigSyntax guessSyntax() {
-            return syntaxFromExtension(input.getName());
+            return ConfigImplUtil.syntaxFromExtension(input.getName());
         }
 
         @Override
@@ -754,7 +745,7 @@ protected AbstractConfigObject rawParseValue(ConfigOrigin origin,
 
         @Override
         ConfigSyntax guessSyntax() {
-            return syntaxFromExtension(resource);
+            return ConfigImplUtil.syntaxFromExtension(resource);
         }
 
         static String parent(String resource) {
diff --git a/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java b/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java
index cf98a900d..941d44f65 100644
--- a/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java
+++ b/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java
@@ -1,8 +1,5 @@
 package com.typesafe.config.impl;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * This exists because we have to memoize resolved substitutions as we go
  * through the config tree; otherwise we could end up creating multiple copies
@@ -11,14 +8,14 @@
 final class ResolveMemos {
     // note that we can resolve things to undefined (represented as Java null,
     // rather than ConfigNull) so this map can have null values.
-    final private Map<MemoKey, AbstractConfigValue> memos;
+    final private BadMap<MemoKey, AbstractConfigValue> memos;
 
-    private ResolveMemos(Map<MemoKey, AbstractConfigValue> memos) {
+    private ResolveMemos(BadMap<MemoKey, AbstractConfigValue> memos) {
         this.memos = memos;
     }
 
     ResolveMemos() {
-        this(new HashMap<MemoKey, AbstractConfigValue>());
+        this(new BadMap<>());
     }
 
     AbstractConfigValue get(MemoKey key) {
@@ -26,10 +23,6 @@ AbstractConfigValue get(MemoKey key) {
     }
 
     ResolveMemos put(MemoKey key, AbstractConfigValue value) {
-        // completely inefficient, but so far nobody cares about resolve()
-        // performance, we can clean it up someday...
-        Map<MemoKey, AbstractConfigValue> copy = new HashMap<MemoKey, AbstractConfigValue>(memos);
-        copy.put(key, value);
-        return new ResolveMemos(copy);
+        return new ResolveMemos(memos.copyingPut(key, value));
     }
 }
diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java
index c31471244..c0d6b65c7 100644
--- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java
+++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java
@@ -7,7 +7,11 @@
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.DateTimeException;
 import java.time.Duration;
+import java.time.Period;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAmount;
 import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -149,7 +153,7 @@ static private AbstractConfigValue findKeyOrNull(AbstractConfigObject self, Stri
             ConfigValueType expected, Path originalPath) {
         AbstractConfigValue v = self.peekAssumingResolved(key, originalPath);
         if (v == null)
-            throw new ConfigException.Missing(originalPath.render());
+            throw new ConfigException.Missing(self.origin(), originalPath.render());
 
         if (expected != null)
             v = DefaultTransformer.transform(v, expected);
@@ -322,6 +326,21 @@ public Duration getDuration(String path) {
         return Duration.ofNanos(nanos);
     }
 
+    @Override
+    public Period getPeriod(String path){
+        ConfigValue v = find(path, ConfigValueType.STRING);
+        return parsePeriod((String) v.unwrapped(), v.origin(), path);
+    }
+
+    @Override
+    public TemporalAmount getTemporal(String path){
+        try{
+            return getDuration(path);
+        } catch (ConfigException.BadValue e){
+            return getPeriod(path);
+        }
+    }
+
     @SuppressWarnings("unchecked")
     private <T> List<T> getHomogeneousUnwrappedList(String path,
             ConfigValueType expected) {
@@ -583,6 +602,90 @@ private static String getUnits(String s) {
         return s.substring(i + 1);
     }
 
+    /**
+     * Parses a period string. If no units are specified in the string, it is
+     * assumed to be in days. The returned period is in days.
+     * The purpose of this function is to implement the period-related methods
+     * in the ConfigObject interface.
+     *
+     * @param input
+     *            the string to parse
+     * @param originForException
+     *            origin of the value being parsed
+     * @param pathForException
+     *            path to include in exceptions
+     * @return duration in days
+     * @throws ConfigException
+     *             if string is invalid
+     */
+    public static Period parsePeriod(String input,
+                                     ConfigOrigin originForException, String pathForException) {
+        String s = ConfigImplUtil.unicodeTrim(input);
+        String originalUnitString = getUnits(s);
+        String unitString = originalUnitString;
+        String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, s.length()
+                - unitString.length()));
+        ChronoUnit units;
+
+        // this would be caught later anyway, but the error message
+        // is more helpful if we check it here.
+        if (numberString.length() == 0)
+            throw new ConfigException.BadValue(originForException,
+                    pathForException, "No number in period value '" + input
+                    + "'");
+
+        if (unitString.length() > 2 && !unitString.endsWith("s"))
+            unitString = unitString + "s";
+
+        // note that this is deliberately case-sensitive
+        if (unitString.equals("") || unitString.equals("d") || unitString.equals("days")) {
+            units = ChronoUnit.DAYS;
+
+        } else if (unitString.equals("w") || unitString.equals("weeks")) {
+            units = ChronoUnit.WEEKS;
+
+        } else if (unitString.equals("m") || unitString.equals("mo") || unitString.equals("months")) {
+            units = ChronoUnit.MONTHS;
+
+        } else if (unitString.equals("y") || unitString.equals("years")) {
+            units = ChronoUnit.YEARS;
+
+        } else {
+            throw new ConfigException.BadValue(originForException,
+                    pathForException, "Could not parse time unit '"
+                    + originalUnitString
+                    + "' (try d, w, mo, y)");
+        }
+
+        try {
+           return periodOf(Integer.parseInt(numberString), units);
+        } catch (NumberFormatException e) {
+            throw new ConfigException.BadValue(originForException,
+                    pathForException, "Could not parse duration number '"
+                    + numberString + "'");
+        }
+    }
+
+
+    private static Period periodOf(int n, ChronoUnit unit){
+        if(unit.isTimeBased()){
+            throw new DateTimeException(unit + " cannot be converted to a java.time.Period");
+        }
+
+        switch (unit){
+            case DAYS:
+                return Period.ofDays(n);
+            case WEEKS:
+                return Period.ofWeeks(n);
+            case MONTHS:
+                return Period.ofMonths(n);
+            case YEARS:
+                return Period.ofYears(n);
+            default:
+                throw new DateTimeException(unit + " cannot be converted to a java.time.Period");
+        }
+    }
+
     /**
      * Parses a duration string. If no units are specified in the string, it is
      * assumed to be in milliseconds. The returned duration is in nanoseconds.
diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java b/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java
index cf5f5ea07..ef69c6885 100644
--- a/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java
+++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java
@@ -5,6 +5,7 @@
 
 import java.io.ObjectStreamException;
 import java.io.Serializable;
+import java.math.BigInteger;
 import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -426,15 +427,14 @@ private static boolean isAllDigits(String s) {
             int length = s.length();
 
             // empty string doesn't count as a number
+            // string longer than "max number of digits in a long" cannot be parsed as a long
             if (length == 0)
                 return false;
 
             for (int i = 0; i < length; ++i) {
                 char c = s.charAt(i);
 
-                if (Character.isDigit(c))
-                    continue;
-                else
+                if (!Character.isDigit(c))
                     return false;
             }
             return true;
@@ -449,7 +449,7 @@ public int compare(String a, String b) {
             boolean aDigits = isAllDigits(a);
             boolean bDigits = isAllDigits(b);
             if (aDigits && bDigits) {
-                return Integer.compare(Integer.parseInt(a), Integer.parseInt(b));
+                return new BigInteger(a).compareTo(new BigInteger(b));
             } else if (aDigits) {
                 return -1;
             } else if (bDigits) {
diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleConfigOrigin.java b/config/src/main/java/com/typesafe/config/impl/SimpleConfigOrigin.java
index a3dc3e4d5..9e05f9e91 100644
--- a/config/src/main/java/com/typesafe/config/impl/SimpleConfigOrigin.java
+++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfigOrigin.java
@@ -364,7 +364,7 @@ static ConfigOrigin mergeOrigins(Collection<? extends ConfigOrigin> stack) {
             Iterator<? extends ConfigOrigin> i = stack.iterator();
             return mergeTwo((SimpleConfigOrigin) i.next(), (SimpleConfigOrigin) i.next());
         } else {
-            List<SimpleConfigOrigin> remaining = new ArrayList<SimpleConfigOrigin>();
+            List<SimpleConfigOrigin> remaining = new ArrayList<SimpleConfigOrigin>(stack.size());
             for (ConfigOrigin o : stack) {
                 remaining.add((SimpleConfigOrigin) o);
             }
diff --git a/config/src/main/java/com/typesafe/config/impl/package.html b/config/src/main/java/com/typesafe/config/impl/package.html
index 4df65c25e..52592b0db 100644
--- a/config/src/main/java/com/typesafe/config/impl/package.html
+++ b/config/src/main/java/com/typesafe/config/impl/package.html
@@ -17,7 +17,7 @@
 you're interested in browsing implementation details. None of the ABI
 under <code>impl</code> has any guarantees; it will change whenever someone
 feels like changing it. If you feel you need access to something
-in <code>impl</code>, <a href="https://github.com/typesafehub/config/issues">please
+in <code>impl</code>, <a href="https://github.com/lightbend/config/issues">please
 file a feature request</a>.
 </p>
 
diff --git a/config/src/main/java/com/typesafe/config/package.html b/config/src/main/java/com/typesafe/config/package.html
index 7d7fe7f29..8f5d9f454 100644
--- a/config/src/main/java/com/typesafe/config/package.html
+++ b/config/src/main/java/com/typesafe/config/package.html
@@ -9,14 +9,14 @@
 <body bgcolor="white">
 
 <p>
-An API for loading and using configuration files, see <a href="https://github.com/typesafehub/config/">the project site</a>
+An API for loading and using configuration files, see <a href="https://github.com/lightbend/config/">the project site</a>
 for more information.
 </p>
 
 <p>
 Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use
 it with methods in the {@link com.typesafe.config.Config} interface. Configuration may be in the form of JSON files,
-Java properties, or <a href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON files</a>; you may also
+Java properties, or <a href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON files</a>; you may also
 build your own configuration in code or from your own file formats.
 </p>
 
@@ -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.
-<br/><strong>Example application code:</strong> <a href="https://github.com/typesafehub/config/tree/master/examples/java/simple-app/src/main">Java</a> and <a href="https://github.com/typesafehub/config/tree/master/examples/scala/simple-app/src/main">Scala</a>.
-<br/>Showing a couple of more special-purpose features, <strong>a more complex example:</strong> <a href="https://github.com/typesafehub/config/tree/master/examples/java/complex-app/src/main">Java</a> and <a href="https://github.com/typesafehub/config/tree/master/examples/scala/complex-app/src/main">Scala</a>.
+<br/><strong>Example application code:</strong> <a href="https://github.com/lightbend/config/tree/master/examples/java/simple-app/src/main">Java</a> and <a href="https://github.com/lightbend/config/tree/master/examples/scala/simple-app/src/main">Scala</a>.
+<br/>Showing a couple of more special-purpose features, <strong>a more complex example:</strong> <a href="https://github.com/lightbend/config/tree/master/examples/java/complex-app/src/main">Java</a> and <a href="https://github.com/lightbend/config/tree/master/examples/scala/complex-app/src/main">Scala</a>.
 </p>
 
 <p>
@@ -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 <code>Config</code> parameter
 and one which uses {@link com.typesafe.config.ConfigFactory#load()}.
-<br/><strong>Example library code:</strong> <a href="https://github.com/typesafehub/config/tree/master/examples/java/simple-lib/src/main">Java</a> and <a href="https://github.com/typesafehub/config/tree/master/examples/scala/simple-lib/src/main">Scala</a>.
+<br/><strong>Example library code:</strong> <a href="https://github.com/lightbend/config/tree/master/examples/java/simple-lib/src/main">Java</a> and <a href="https://github.com/lightbend/config/tree/master/examples/scala/simple-lib/src/main">Scala</a>.
 </p>
 
 <p>
-Check out the full <a href="https://github.com/typesafehub/config/tree/master/examples">examples directory on GitHub</a>.
+Check out the full <a href="https://github.com/lightbend/config/tree/master/examples">examples directory on GitHub</a>.
 </p>
 
 <p>
 What else to read:
 <ul>
 	<li>The overview documentation for interface {@link com.typesafe.config.Config}.</li>
-	<li>The <a href="https://github.com/typesafehub/config/blob/master/README.md">README</a> for the library.</li>
+	<li>The <a href="https://github.com/lightbend/config/blob/master/README.md">README</a> for the library.</li>
 	<li>If you want to use <code>.conf</code> files in addition to <code>.json</code> and <code>.properties</code>,
-	see the <a href="https://github.com/typesafehub/config/blob/master/README.md">README</a> for some short examples
-	and the full <a href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON spec</a> for the long version.</li>
+	see the <a href="https://github.com/lightbend/config/blob/master/README.md">README</a> for some short examples
+	and the full <a href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON spec</a> for the long version.</li>
 </ul>
 </p>
 
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: <a
- * href="https://github.com/typesafehub/config/issues/300"
- * >https://github.com/typesafehub/config/issues/300</a>
+ * href="https://github.com/lightbend/config/issues/300"
+ * >https://github.com/lightbend/config/issues/300</a>
  *
  * <p>
  * 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 <code>com.typesafe.config</code> 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 <a href="https://github.com/typesafehub/config/">the project site</a>.
+see <a href="https://github.com/lightbend/config/">the project site</a>.
 </p>
 
 <p>
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<Integer> empty;
+    Set<Integer> ofInt;
+    Set<String> ofString;
+    Set<Double> ofDouble;
+    Set<Long> ofLong;
+    Set<Object> ofNull;
+    Set<Boolean> ofBoolean;
+    Set<Object> ofObject;
+    Set<Config> ofConfig;
+    Set<ConfigObject> ofConfigObject;
+    Set<ConfigValue> ofConfigValue;
+    Set<Duration> ofDuration;
+    Set<ConfigMemorySize> ofMemorySize;
+    Set<StringsConfig> ofStringBean;
+
+    public Set<Integer> getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(Set<Integer> empty) {
+        this.empty = empty;
+    }
+
+    public Set<Integer> getOfInt() {
+        return ofInt;
+    }
+
+    public void setOfInt(Set<Integer> ofInt) {
+        this.ofInt = ofInt;
+    }
+
+    public Set<String> getOfString() {
+        return ofString;
+    }
+
+    public void setOfString(Set<String> ofString) {
+        this.ofString = ofString;
+    }
+
+    public Set<Double> getOfDouble() {
+        return ofDouble;
+    }
+
+    public void setOfDouble(Set<Double> ofDouble) {
+        this.ofDouble = ofDouble;
+    }
+
+    public Set<Object> getOfNull() {
+        return ofNull;
+    }
+
+    public void setOfNull(Set<Object> ofNull) {
+        this.ofNull = ofNull;
+    }
+
+    public Set<Boolean> getOfBoolean() {
+        return ofBoolean;
+    }
+
+    public void setOfBoolean(Set<Boolean> ofBoolean) {
+        this.ofBoolean = ofBoolean;
+    }
+
+    public Set<Object> getOfObject() {
+        return ofObject;
+    }
+
+    public void setOfObject(Set<Object> ofObject) {
+        this.ofObject = ofObject;
+    }
+
+    public Set<Long> getOfLong() {
+        return ofLong;
+    }
+
+    public void setOfLong(Set<Long> ofLong) {
+        this.ofLong = ofLong;
+    }
+
+    public Set<Config> getOfConfig() {
+        return ofConfig;
+    }
+
+    public void setOfConfig(Set<Config> ofConfig) {
+        this.ofConfig = ofConfig;
+    }
+
+    public Set<ConfigObject> getOfConfigObject() {
+        return ofConfigObject;
+    }
+
+    public void setOfConfigObject(Set<ConfigObject> ofConfigObject) {
+        this.ofConfigObject = ofConfigObject;
+    }
+
+    public Set<ConfigValue> getOfConfigValue() {
+        return ofConfigValue;
+    }
+
+    public void setOfConfigValue(Set<ConfigValue> ofConfigValue) {
+        this.ofConfigValue = ofConfigValue;
+    }
+
+    public Set<Duration> getOfDuration() {
+        return ofDuration;
+    }
+
+    public void setOfDuration(Set<Duration> ofDuration) {
+        this.ofDuration = ofDuration;
+    }
+
+    public Set<ConfigMemorySize> getOfMemorySize() {
+        return ofMemorySize;
+    }
+
+    public void setOfMemorySize(Set<ConfigMemorySize> ofMemorySize) {
+        this.ofMemorySize = ofMemorySize;
+    }
+
+    public Set<StringsConfig> getOfStringBean() {
+        return ofStringBean;
+    }
+
+    public void setOfStringBean(Set<StringsConfig> ofStringBean) {
+        this.ofStringBean = ofStringBean;
+    }
+}
diff --git a/config/src/test/resources/beanconfig/beanconfig01.conf b/config/src/test/resources/beanconfig/beanconfig01.conf
index 791553dea..2a2ea2ed5 100644
--- a/config/src/test/resources/beanconfig/beanconfig01.conf
+++ b/config/src/test/resources/beanconfig/beanconfig01.conf
@@ -101,5 +101,31 @@
       "valueObject": {
         "mandatoryValue": "notNull"
       }
+    },
+    "sets" : {
+      "empty" : [],
+      "ofInt" : [1, 2, 3, 2, 3],
+      "ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
+      "of-double" : [3.14, 4.14, 4.14, 5.14],
+      "of-long" : { "1" : 32, "2" : 42, "3" : 52 }, // object-to-list conversion
+      "ofNull" : [null, null, null],
+      "ofBoolean" : [true, false, false],
+      "ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
+      "ofObject" : [${numbers}, ${booleans}, ${strings}],
+      "ofConfig" : [${numbers}, ${booleans}, ${strings}],
+      "ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
+      "ofConfigValue" : [1, 2, "a"],
+      "ofDuration" : [1, 2h, 3 days],
+      "ofMemorySize" : [1024, 1M, 1G],
+      "ofStringBean" : [
+        {
+          abcd : "testAbcdOne"
+          yes : "testYesOne"
+        },
+        {
+          abcd : "testAbcdTwo"
+          yes : "testYesTwo"
+        }
+      ]
     }
 }
diff --git a/config/src/test/resources/test01.conf b/config/src/test/resources/test01.conf
index 1370bf852..e40eb7f5d 100644
--- a/config/src/test/resources/test01.conf
+++ b/config/src/test/resources/test01.conf
@@ -65,6 +65,14 @@
         "minusLargeNanos" : -4878955355435272204ns
     },
 
+    "periods" : {
+        "day" : 1d,
+        "dayAsNumber": 2,
+        "week": 3 weeks,
+        "month": 5 mo,
+        "year": 8y
+    },
+
     "memsizes" : {
         "meg" : 1M,
         "megsList" : [1M, 1024K, 1048576],
diff --git a/config/src/test/resources/test01.properties b/config/src/test/resources/test01.properties
index 94eec4470..71de4e0be 100644
--- a/config/src/test/resources/test01.properties
+++ b/config/src/test/resources/test01.properties
@@ -2,3 +2,4 @@
 fromProps.abc=abc
 fromProps.one=1
 fromProps.bool=true
+fromProps.specialChars=hello^^
diff --git a/config/src/test/resources/test04.conf b/config/src/test/resources/test04.conf
index 1aa55df07..cf024f00f 100644
--- a/config/src/test/resources/test04.conf
+++ b/config/src/test/resources/test04.conf
@@ -174,7 +174,7 @@ akka {
 
       mongodb {
         # Any specified collection name will be used as a prefix for collections that use durable mongo mailboxes
-        uri = "mongodb://localhost/akka.mailbox" # Follow Mongo URI Spec - http://www.mongodb.org/display/DOCS/Connections
+        uri = "mongodb://localhost/akka.mailbox" # Follow Mongo URI Spec - https://docs.mongodb.com/manual/reference/connection-string/
 
         # Configurable timeouts for certain ops
         timeout {
diff --git a/config/src/test/resources/test12.conf b/config/src/test/resources/test12.conf
new file mode 100644
index 000000000..b7aff622b
--- /dev/null
+++ b/config/src/test/resources/test12.conf
@@ -0,0 +1,43 @@
+// this checks sorting map keys, where keys that look like numbers are treated differently
+// specifically tests very long numbers which fit neither in an Integer nor in a Long
+
+"10" = "42"
+sth = 42
+"1" = "42"
+"12" = "42"
+"123" = "42"
+"1234" = "42"
+"12345" = "42"
+"123456" = "42"
+"1234567" = "42"
+"12345678" = "42"
+"123456789" = "42"
+"1234567890" = "42"
+"12345678901" = "42"
+"123456789012" = "42"
+"1234567890123" = "42"
+"12345678901234" = "42"
+"123456789012345" = "42"
+"1234567890123456" = "42"
+"12345678901234567" = "42"
+"123456789012345678" = "42"
+"1234567890123456789" = "42"
+"12345678901234567891" = "42"
+"123456789012345678912" = "42"
+"1234567890123456789123" = "42"
+"12345678901234567891234" = "42"
+"123456789012345678912345" = "42"
+"1234567890123456789123456" = "42"
+"12345678901234567891234567" = "42"
+"123456789012345678912345678" = "42"
+"1234567890123456789123456789" = "42"
+"12345678901234567891234567890" = "42"
+"123456789012345678912345678901" = "42"
+"1234567890123456789123456789012" = "42"
+"12345678901234567891234567890123" = "42"
+"123456789012345678912345678901234" = "42"
+"1234567890123456789123456789012345" = "42"
+"12345678901234567891234567890123456" = "42"
+"123456789012345678912345678901234567" = "42"
+"1234567890123456789123456789012345678" = "42"
+"12345678901234567891234567890123456789" = "42"
\ No newline at end of file
diff --git a/config/src/test/resources/test13-application-override-substitutions.conf b/config/src/test/resources/test13-application-override-substitutions.conf
new file mode 100644
index 000000000..506b2cada
--- /dev/null
+++ b/config/src/test/resources/test13-application-override-substitutions.conf
@@ -0,0 +1 @@
+b = "overridden"
\ No newline at end of file
diff --git a/config/src/test/resources/test13-reference-bad-substitutions.conf b/config/src/test/resources/test13-reference-bad-substitutions.conf
new file mode 100644
index 000000000..1b2615077
--- /dev/null
+++ b/config/src/test/resources/test13-reference-bad-substitutions.conf
@@ -0,0 +1 @@
+a = ${b}
\ No newline at end of file
diff --git a/config/src/test/resources/test13-reference-with-substitutions.conf b/config/src/test/resources/test13-reference-with-substitutions.conf
new file mode 100644
index 000000000..fcf7f995c
--- /dev/null
+++ b/config/src/test/resources/test13-reference-with-substitutions.conf
@@ -0,0 +1,2 @@
+a = ${b}
+b = "b"
\ No newline at end of file
diff --git a/config/src/test/scala/ApiExamples.scala b/config/src/test/scala/ApiExamples.scala
index 2a6fce76a..5d464dce3 100644
--- a/config/src/test/scala/ApiExamples.scala
+++ b/config/src/test/scala/ApiExamples.scala
@@ -52,7 +52,7 @@ class ApiExamples {
         class EnhancedConfig(c: Config) {
             def getAny(path: String): Any = c.getAnyRef(path)
         }
-        implicit def config2enhanced(c: Config) = new EnhancedConfig(c)
+        implicit def config2enhanced(c: Config): EnhancedConfig = new EnhancedConfig(c)
 
         // somewhat nicer now
         val e: Int = conf.getAny("ints.fortyTwo") match {
diff --git a/config/src/test/scala/Profiling.scala b/config/src/test/scala/Profiling.scala
index 5cd412ef1..e66768b5f 100644
--- a/config/src/test/scala/Profiling.scala
+++ b/config/src/test/scala/Profiling.scala
@@ -36,7 +36,7 @@ object Util {
     }
 
     def time(body: () => Unit, iterations: Int): Double = {
-        timeHelper(body, iterations, false)
+        timeHelper(body, iterations, retried = false)
     }
 
     def loop(args: Seq[String], body: () => Unit) {
@@ -57,10 +57,10 @@ object FileLoad extends App {
         }
     }
 
-    val ms = Util.time(task, 4000)
+    val ms = Util.time(() => task(), 4000)
     println("file load: " + ms + "ms")
 
-    Util.loop(args, task)
+    Util.loop(args, () => task())
 }
 
 object Resolve extends App {
@@ -73,10 +73,10 @@ object Resolve extends App {
         }
     }
 
-    val ms = Util.time(task, 3000000)
+    val ms = Util.time(() => task(), 3000000)
     println("resolve: " + ms + "ms")
 
-    Util.loop(args, task)
+    Util.loop(args, () => task())
 }
 
 object GetExistingPath extends App {
@@ -88,10 +88,10 @@ object GetExistingPath extends App {
         }
     }
 
-    val ms = Util.time(task, 2000000)
+    val ms = Util.time(() => task(), 2000000)
     println("GetExistingPath: " + ms + "ms")
 
-    Util.loop(args, task)
+    Util.loop(args, () => task())
 }
 
 object GetSeveralExistingPaths extends App {
@@ -105,10 +105,10 @@ object GetSeveralExistingPaths extends App {
         }
     }
 
-    val ms = Util.time(task, 5000000)
+    val ms = Util.time(() => task(), 5000000)
     println("GetSeveralExistingPaths: " + ms + "ms")
 
-    Util.loop(args, task)
+    Util.loop(args, () => task())
 }
 
 object HasPathOnMissing extends App {
@@ -120,10 +120,10 @@ object HasPathOnMissing extends App {
         }
     }
 
-    val ms = Util.time(task, 20000000)
+    val ms = Util.time(() => task(), 20000000)
     println("HasPathOnMissing: " + ms + "ms")
 
-    Util.loop(args, task)
+    Util.loop(args, () => task())
 }
 
 object CatchExceptionOnMissing extends App {
@@ -146,9 +146,9 @@ object CatchExceptionOnMissing extends App {
     }
 
     anotherStackFrame(40) { () =>
-        val ms = Util.time(task, 300000)
+        val ms = Util.time(() => task(), 300000)
         println("CatchExceptionOnMissing: " + ms + "ms")
 
-        Util.loop(args, task)
+        Util.loop(args, () => task())
     }
 }
diff --git a/config/src/test/scala/Rendering.scala b/config/src/test/scala/Rendering.scala
index 0d8be469e..1cc264be0 100644
--- a/config/src/test/scala/Rendering.scala
+++ b/config/src/test/scala/Rendering.scala
@@ -33,6 +33,7 @@ object RenderExample extends App {
     render("test01")
     render("test06")
     render("test05")
+    render("test12")
 }
 
 object RenderOptions extends App {
@@ -62,7 +63,7 @@ object RenderOptions extends App {
 
     val rendered =
         allBooleanLists(4).foldLeft(0) { (count, values) =>
-            val formatted = values(0)
+            val formatted = values.head
             val originComments = values(1)
             val comments = values(2)
             val json = values(3)
diff --git a/config/src/test/scala/com/typesafe/config/impl/BadMapTest.scala b/config/src/test/scala/com/typesafe/config/impl/BadMapTest.scala
new file mode 100644
index 000000000..bfc7af173
--- /dev/null
+++ b/config/src/test/scala/com/typesafe/config/impl/BadMapTest.scala
@@ -0,0 +1,94 @@
+package com.typesafe.config.impl
+
+import org.junit.Assert._
+import org.junit.Test
+
+class BadMapTest extends TestUtils {
+    @Test
+    def copyingPut(): Unit = {
+        val map = new BadMap[String, String]()
+        val copy = map.copyingPut("key", "value")
+
+        assertNull(map.get("key"))
+        assertEquals("value", copy.get("key"))
+    }
+
+    @Test
+    def retrieveOldElement(): Unit = {
+        val map = new BadMap[String, String]()
+            .copyingPut("key1", "value1")
+            .copyingPut("key2", "value2")
+            .copyingPut("key3", "value3")
+
+        assertEquals("value1", map.get("key1"))
+        assertEquals("value2", map.get("key2"))
+        assertEquals("value3", map.get("key3"))
+    }
+
+    @Test
+    def putOverride(): Unit = {
+        val map = new BadMap[String, String]()
+            .copyingPut("key", "value1")
+            .copyingPut("key", "value2")
+            .copyingPut("key", "value3")
+
+        assertEquals("value3", map.get("key"))
+    }
+
+    @Test
+    def notFound(): Unit = {
+        val map = new BadMap[String, String]()
+
+        assertNull(map.get("invalid key"))
+    }
+
+    @Test
+    def putMany(): Unit = {
+        val entries = (1 to 1000).map(i => (s"key$i", s"value$i"))
+        var map = new BadMap[String, String]()
+
+        for ((key, value) <- entries) {
+            map = map.copyingPut(key, value)
+        }
+
+        for ((key, value) <- entries) {
+            assertEquals(value, map.get(key))
+        }
+    }
+
+    @Test
+    def putSameHash(): Unit = {
+        val hash = 2
+        val entries = (1 to 10).map(i => (new UniqueKeyWithHash(hash), s"value$i"))
+        var map = new BadMap[UniqueKeyWithHash, String]()
+
+        for ((key, value) <- entries) {
+            map = map.copyingPut(key, value)
+        }
+
+        for ((key, value) <- entries) {
+            assertEquals(value, map.get(key))
+        }
+    }
+
+    @Test
+    def putSameHashModLength(): Unit = {
+        // given that the table will eventually be the following size, we insert entries who should
+        // eventually all share the same index and then later be redistributed once rehashed
+        val size = 11
+        val entries = (1 to size * 2).map(i => (new UniqueKeyWithHash(size * i), s"value$i"))
+        var map = new BadMap[UniqueKeyWithHash, String]()
+
+        for ((key, value) <- entries) {
+            map = map.copyingPut(key, value)
+        }
+
+        for ((key, value) <- entries) {
+            assertEquals(value, map.get(key))
+        }
+    }
+
+    private class UniqueKeyWithHash(hash: Int) {
+        override def hashCode(): Int = hash
+    }
+}
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConcatenationTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConcatenationTest.scala
index aa70347e7..acf898c3f 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConcatenationTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConcatenationTest.scala
@@ -346,7 +346,7 @@ class ConcatenationTest extends TestUtils {
     }
 
     // We would ideally make this case NOT throw an exception but we need to do some work
-    // to get there, see https://github.com/typesafehub/config/issues/160
+    // to get there, see https://github.com/lightbend/config/issues/160
     @Test
     def plusEqualsMultipleTimesNestedInArray() {
         val e = intercept[ConfigException.Parse] {
@@ -357,7 +357,7 @@ class ConcatenationTest extends TestUtils {
     }
 
     // We would ideally make this case NOT throw an exception but we need to do some work
-    // to get there, see https://github.com/typesafehub/config/issues/160
+    // to get there, see https://github.com/lightbend/config/issues/160
     @Test
     def plusEqualsMultipleTimesNestedInPlusEquals() {
         val e = intercept[ConfigException.Parse] {
@@ -367,7 +367,7 @@ class ConcatenationTest extends TestUtils {
         assertTrue(e.getMessage.contains("limitation"))
     }
 
-    // from https://github.com/typesafehub/config/issues/177
+    // from https://github.com/lightbend/config/issues/177
     @Test
     def arrayConcatenationInDoubleNestedDelayedMerge() {
         val unresolved = parseConfig("""d { x = [] }, c : ${d}, c { x += 1, x += 2 }""")
@@ -375,7 +375,7 @@ class ConcatenationTest extends TestUtils {
         assertEquals(Seq(1, 2), conf.getIntList("c.x").asScala)
     }
 
-    // from https://github.com/typesafehub/config/issues/177
+    // from https://github.com/lightbend/config/issues/177
     @Test
     def arrayConcatenationAsPartOfDelayedMerge() {
         val unresolved = parseConfig(""" c { x: [], x : ${c.x}[1], x : ${c.x}[2] }""")
@@ -383,7 +383,7 @@ class ConcatenationTest extends TestUtils {
         assertEquals(Seq(1, 2), conf.getIntList("c.x").asScala)
     }
 
-    // from https://github.com/typesafehub/config/issues/177
+    // from https://github.com/lightbend/config/issues/177
     @Test
     def arrayConcatenationInDoubleNestedDelayedMerge2() {
         val unresolved = parseConfig("""d { x = [] }, c : ${d}, c { x : ${c.x}[1], x : ${c.x}[2] }""")
@@ -391,7 +391,7 @@ class ConcatenationTest extends TestUtils {
         assertEquals(Seq(1, 2), conf.getIntList("c.x").asScala)
     }
 
-    // from https://github.com/typesafehub/config/issues/177
+    // from https://github.com/lightbend/config/issues/177
     @Test
     def arrayConcatenationInTripleNestedDelayedMerge() {
         val unresolved = parseConfig("""{ r: { d.x=[] }, q: ${r}, q : { d { x = [] }, c : ${q.d}, c { x : ${q.c.x}[1], x : ${q.c.x}[2] } } }""")
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala
index 3ff873771..648e0c4ee 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala
@@ -15,14 +15,14 @@ import java.util.Properties
 
 class ConfParserTest extends TestUtils {
 
-    def parseWithoutResolving(s: String) = {
+    def parseWithoutResolving(s: String): AbstractConfigValue = {
         val options = ConfigParseOptions.defaults().
             setOriginDescription("test conf string").
             setSyntax(ConfigSyntax.CONF)
-        Parseable.newString(s, options).parseValue().asInstanceOf[AbstractConfigValue]
+        Parseable.newString(s, options).parseValue()
     }
 
-    def parse(s: String) = {
+    def parse(s: String): AbstractConfigValue = {
         val tree = parseWithoutResolving(s)
 
         // resolve substitutions so we can test problems with that, like cycles or
@@ -38,7 +38,7 @@ class ConfParserTest extends TestUtils {
     @Test
     def invalidConfThrows(): Unit = {
         // be sure we throw
-        for (invalid <- whitespaceVariations(invalidConf, false)) {
+        for (invalid <- whitespaceVariations(invalidConf, validInLift = false)) {
             addOffendingJsonToException("config", invalid.test) {
                 intercept[ConfigException] {
                     parse(invalid.test)
@@ -152,7 +152,7 @@ class ConfParserTest extends TestUtils {
                 }
             } catch {
                 case e: Throwable =>
-                    System.err.println("failed on: '" + invalid + "'");
+                    System.err.println("failed on: '" + invalid + "'")
                     throw e;
             }
         }
@@ -267,9 +267,9 @@ class ConfParserTest extends TestUtils {
             { s: String => s.replace(",\n", "  \n  \n  ,  \n  \n  ") },
             { s: String => dropCurlies(s) })
 
-        var tested = 0;
+        var tested = 0
         for (v <- valids; change <- changes) {
-            tested += 1;
+            tested += 1
             val obj = parseConfig(change(v))
             assertEquals(3, obj.root.size())
             assertEquals("y", obj.getString("a"))
@@ -351,7 +351,7 @@ class ConfParserTest extends TestUtils {
     }
 
     @Test
-    def toStringForParseables() {
+    def toStringForParseablesWorks() {
         // just be sure the toString don't throw, to get test coverage
         val options = ConfigParseOptions.defaults()
         Parseable.newFile(new File("foo"), options).toString
@@ -366,7 +366,7 @@ class ConfParserTest extends TestUtils {
     }
 
     private def assertComments(comments: Seq[String], conf: Config, path: String) {
-        assertEquals(comments, conf.getValue(path).origin().comments().asScala.toSeq)
+        assertEquals(comments, conf.getValue(path).origin().comments().asScala)
     }
 
     private def assertComments(comments: Seq[String], conf: Config, path: String, index: Int) {
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala
index 8c85e4550..b7e56ebf7 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala
@@ -7,7 +7,7 @@ import beanconfig.EnumsConfig.{ Solution, Problem }
 import com.typesafe.config._
 
 import java.io.{ InputStream, InputStreamReader }
-import java.time.Duration;
+import java.time.Duration
 
 import beanconfig._
 import org.junit.Assert._
@@ -19,7 +19,7 @@ import scala.collection.mutable.ArrayBuffer
 class ConfigBeanFactoryTest extends TestUtils {
 
     @Test
-    def toCamelCase() {
+    def testToCamelCase() {
         assertEquals("configProp", ConfigImplUtil.toCamelCase("config-prop"))
         assertEquals("configProp", ConfigImplUtil.toCamelCase("configProp"))
         assertEquals("fooBar", ConfigImplUtil.toCamelCase("foo-----bar"))
@@ -122,16 +122,49 @@ class ConfigBeanFactoryTest extends TestUtils {
             ConfigMemorySize.ofBytes(1073741824)),
             beanConfig.getOfMemorySize.asScala)
 
-        val stringsConfigOne = new StringsConfig();
+        val stringsConfigOne = new StringsConfig()
         stringsConfigOne.setAbcd("testAbcdOne")
         stringsConfigOne.setYes("testYesOne")
-        val stringsConfigTwo = new StringsConfig();
+        val stringsConfigTwo = new StringsConfig()
         stringsConfigTwo.setAbcd("testAbcdTwo")
         stringsConfigTwo.setYes("testYesTwo")
 
         assertEquals(List(stringsConfigOne, stringsConfigTwo).asJava, beanConfig.getOfStringBean)
     }
 
+    @Test
+    def testCreateSet() {
+        val beanConfig: SetsConfig = ConfigBeanFactory.create(loadConfig().getConfig("sets"), classOf[SetsConfig])
+        assertNotNull(beanConfig)
+        assertEquals(Set().asJava, beanConfig.getEmpty)
+        assertEquals(Set(1, 2, 3).asJava, beanConfig.getOfInt)
+        assertEquals(Set(32L, 42L, 52L).asJava, beanConfig.getOfLong)
+        assertEquals(Set("a", "b", "c").asJava, beanConfig.getOfString)
+        assertEquals(3, beanConfig.getOfObject.size)
+        assertEquals(3, beanConfig.getOfDouble.size)
+        assertEquals(3, beanConfig.getOfConfig.size)
+        assertTrue(beanConfig.getOfConfig.iterator().next().isInstanceOf[Config])
+        assertEquals(3, beanConfig.getOfConfigObject.size)
+        assertTrue(beanConfig.getOfConfigObject.iterator().next().isInstanceOf[ConfigObject])
+        assertEquals(Set(intValue(1), intValue(2), stringValue("a")),
+            beanConfig.getOfConfigValue.asScala)
+        assertEquals(Set(Duration.ofMillis(1), Duration.ofHours(2), Duration.ofDays(3)),
+            beanConfig.getOfDuration.asScala)
+        assertEquals(Set(ConfigMemorySize.ofBytes(1024),
+            ConfigMemorySize.ofBytes(1048576),
+            ConfigMemorySize.ofBytes(1073741824)),
+            beanConfig.getOfMemorySize.asScala)
+
+        val stringsConfigOne = new StringsConfig()
+        stringsConfigOne.setAbcd("testAbcdOne")
+        stringsConfigOne.setYes("testYesOne")
+        val stringsConfigTwo = new StringsConfig()
+        stringsConfigTwo.setAbcd("testAbcdTwo")
+        stringsConfigTwo.setYes("testYesTwo")
+
+        assertEquals(Set(stringsConfigOne, stringsConfigTwo).asJava, beanConfig.getOfStringBean)
+    }
+
     @Test
     def testCreateDuration() {
         val beanConfig: DurationsConfig = ConfigBeanFactory.create(loadConfig().getConfig("durations"), classOf[DurationsConfig])
@@ -167,7 +200,7 @@ class ConfigBeanFactoryTest extends TestUtils {
         assertEquals("abcd", beanConfig.getConfig.getString("abcd"))
         assertEquals(3, beanConfig.getConfigObj.toConfig.getInt("intVal"))
         assertEquals(stringValue("hello world"), beanConfig.getConfigValue)
-        assertEquals(List(1, 2, 3).map(intValue(_)), beanConfig.getList.asScala)
+        assertEquals(List(1, 2, 3).map(intValue), beanConfig.getList.asScala)
         assertEquals(true, beanConfig.getUnwrappedMap.get("shouldBeInt"))
         assertEquals(42, beanConfig.getUnwrappedMap.get("should-be-boolean"))
     }
@@ -178,6 +211,7 @@ class ConfigBeanFactoryTest extends TestUtils {
         assertNotNull(beanConfig)
         assertNotNull(beanConfig.getValueObject)
         assertNull(beanConfig.getValueObject.getOptionalValue)
+        assertNull(beanConfig.getValueObject.getDefault)
         assertEquals("notNull", beanConfig.getValueObject.getMandatoryValue)
     }
 
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentParserTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentParserTest.scala
index fb8bca20b..9cba2eb0a 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentParserTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentParserTest.scala
@@ -1,6 +1,7 @@
 package com.typesafe.config.impl
 
-import com.typesafe.config.{ ConfigException, ConfigSyntax, ConfigParseOptions }
+import com.typesafe.config.ConfigSyntax.JSON
+import com.typesafe.config.{ ConfigException, ConfigParseOptions, ConfigSyntax }
 import org.junit.Assert._
 import org.junit.Test
 
@@ -14,7 +15,7 @@ class ConfigDocumentParserTest extends TestUtils {
     private def parseJSONFailuresTest(origText: String, containsMessage: String) {
         var exceptionThrown = false
         val e = intercept[ConfigException] {
-            ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
+            ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON))
         }
         assertTrue(e.getMessage.contains(containsMessage))
     }
@@ -25,7 +26,7 @@ class ConfigDocumentParserTest extends TestUtils {
         assertEquals(expectedRenderedText, node.render())
         assertTrue(node.isInstanceOf[ConfigNodeSimpleValue])
 
-        val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
+        val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON))
         assertEquals(expectedRenderedText, nodeJSON.render())
         assertTrue(nodeJSON.isInstanceOf[ConfigNodeSimpleValue])
     }
@@ -35,7 +36,7 @@ class ConfigDocumentParserTest extends TestUtils {
         assertEquals(origText, node.render())
         assertTrue(node.isInstanceOf[ConfigNodeComplexValue])
 
-        val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
+        val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON))
         assertEquals(origText, nodeJSON.render())
         assertTrue(nodeJSON.isInstanceOf[ConfigNodeComplexValue])
     }
@@ -45,7 +46,7 @@ class ConfigDocumentParserTest extends TestUtils {
         assertEquals(origText, node.render())
 
         val e = intercept[ConfigException] {
-            ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
+            ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON))
         }
         assertTrue(e.getMessage.contains(containsMessage))
     }
@@ -58,7 +59,7 @@ class ConfigDocumentParserTest extends TestUtils {
     }
 
     @Test
-    def parseSuccess {
+    def parseSuccess() {
         parseTest("foo:bar")
         parseTest(" foo : bar ")
         parseTest("""include "foo.conf" """)
@@ -185,7 +186,7 @@ class ConfigDocumentParserTest extends TestUtils {
               ]
            }
       """
-        val node = ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
+        val node = ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON))
         assertEquals(origText, node.render())
     }
 
@@ -249,7 +250,7 @@ class ConfigDocumentParserTest extends TestUtils {
     }
 
     @Test
-    def parseSingleValuesFailures {
+    def parseSingleValuesFailures() {
         // Parse Simple Value throws on leading and trailing whitespace, comments, or newlines
         parseLeadingTrailingFailure("   123")
         parseLeadingTrailingFailure("123   ")
@@ -267,17 +268,17 @@ class ConfigDocumentParserTest extends TestUtils {
 
         // Check that concatenations in JSON will throw an error
         var origText = "123 456 \"abc\""
-        var e = intercept[ConfigException] { ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON)) }
+        var e = intercept[ConfigException] { ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON)) }
         assertTrue("expected message for parsing concat as json", e.getMessage.contains("Parsing JSON and the value set in withValueText was either a concatenation or had trailing whitespace, newlines, or comments"))
 
         // Check that keys with no separators and object values in JSON will throw an error
         origText = """{"foo" { "bar" : 12 } }"""
-        e = intercept[ConfigException] { ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax((ConfigSyntax.JSON))) }
+        e = intercept[ConfigException] { ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(JSON)) }
         assertTrue("expected failure for key foo followed by token", e.getMessage.contains("""Key '"foo"' may not be followed by token: '{'"""))
     }
 
     @Test
-    def parseEmptyDocument {
+    def parseEmptyDocument() {
         val node = ConfigDocumentParser.parse(tokenize(""), fakeOrigin(), ConfigParseOptions.defaults())
         assertTrue(node.value().isInstanceOf[ConfigNodeObject])
         assertTrue(node.value().children().isEmpty())
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentTest.scala
index 1106f6041..8811d64b4 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigDocumentTest.scala
@@ -28,7 +28,7 @@ class ConfigDocumentTest extends TestUtils {
     }
 
     @Test
-    def configDocumentReplace {
+    def configDocumentReplace() {
         // Can handle parsing/replacement with a very simple map
         configDocumentReplaceConfTest("""{"a":1}""", """{"a":2}""", "2", "a")
         configDocumentReplaceJsonTest("""{"a":1}""", """{"a":2}""", "2", "a")
@@ -37,7 +37,7 @@ class ConfigDocumentTest extends TestUtils {
         configDocumentReplaceConfTest("a: b\nc = d", "a: b\nc = 12", "12", "c")
 
         // Can handle parsing/replacement with a complicated map
-        var origText =
+        val origText =
             """{
               "a":123,
               "b": 123.456,
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigSubstitutionTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigSubstitutionTest.scala
index 712e1c438..61047c405 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigSubstitutionTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigSubstitutionTest.scala
@@ -79,7 +79,7 @@ class ConfigSubstitutionTest extends TestUtils {
     def resolveNull() {
         val s = subst("bar.null")
         val v = resolveWithoutFallbacks(s, simpleObject)
-        assertEquals(nullValue(), v)
+        assertEquals(nullValue, v)
     }
 
     @Test
@@ -126,13 +126,13 @@ class ConfigSubstitutionTest extends TestUtils {
 
     @Test
     def resolveMissingInString() {
-        val s = substInString("bar.missing", true /* optional */ )
+        val s = substInString("bar.missing", optional = true)
         val v = resolveWithoutFallbacks(s, simpleObject)
         // absent object becomes empty string
         assertEquals(stringValue("start<>end"), v)
 
         intercept[ConfigException.UnresolvedSubstitution] {
-            val s2 = substInString("bar.missing", false /* optional */ )
+            val s2 = substInString("bar.missing", optional = false)
             resolveWithoutFallbacks(s2, simpleObject)
         }
     }
@@ -654,9 +654,9 @@ class ConfigSubstitutionTest extends TestUtils {
 
         values.put("a", child.relativized(new Path("a")))
         // this "foo" should NOT be used.
-        values.put("foo", stringValue("in parent"));
+        values.put("foo", stringValue("in parent"))
 
-        val resolved = resolve(new SimpleConfigObject(fakeOrigin(), values));
+        val resolved = resolve(new SimpleConfigObject(fakeOrigin(), values))
 
         assertEquals("in child", resolved.getString("a.bar"))
     }
@@ -670,9 +670,9 @@ class ConfigSubstitutionTest extends TestUtils {
 
         values.put("a", child.relativized(new Path("a")))
         // so this "foo" SHOULD be used
-        values.put("foo", stringValue("in parent"));
+        values.put("foo", stringValue("in parent"))
 
-        val resolved = resolve(new SimpleConfigObject(fakeOrigin(), values));
+        val resolved = resolve(new SimpleConfigObject(fakeOrigin(), values))
 
         assertEquals("in parent", resolved.getString("a.bar"))
     }
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
index d88b365c4..eeda74e70 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
@@ -3,13 +3,16 @@
  */
 package com.typesafe.config.impl
 
+import java.time.temporal.{ ChronoUnit, TemporalUnit }
+
 import org.junit.Assert._
 import org.junit._
 import com.typesafe.config._
 import java.util.concurrent.TimeUnit
+
 import scala.collection.JavaConverters._
 import com.typesafe.config.ConfigResolveOptions
-import java.util.concurrent.TimeUnit.{ SECONDS, NANOSECONDS, MICROSECONDS, MILLISECONDS, MINUTES, DAYS, HOURS }
+import java.util.concurrent.TimeUnit.{ DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS }
 
 class ConfigTest extends TestUtils {
 
@@ -22,7 +25,7 @@ class ConfigTest extends TestUtils {
             ConfigResolveOptions.noSystem()).asInstanceOf[AbstractConfigObject].toConfig
     }
 
-    def mergeUnresolved(toMerge: AbstractConfigObject*) = {
+    def mergeUnresolved(toMerge: AbstractConfigObject*): AbstractConfigObject = {
         if (toMerge.isEmpty) {
             SimpleConfigObject.empty()
         } else {
@@ -30,7 +33,7 @@ class ConfigTest extends TestUtils {
         }
     }
 
-    def merge(toMerge: AbstractConfigObject*) = {
+    def merge(toMerge: AbstractConfigObject*): AbstractConfigObject = {
         val obj = mergeUnresolved(toMerge: _*)
         resolveNoSystem(obj, obj) match {
             case x: AbstractConfigObject => x
@@ -43,34 +46,31 @@ class ConfigTest extends TestUtils {
         def makeTrees(objects: Seq[AbstractConfigObject]): Iterator[AbstractConfigObject] = {
             objects.length match {
                 case 0 => Iterator.empty
-                case 1 => {
+                case 1 =>
                     Iterator(objects(0))
-                }
-                case 2 => {
+                case 2 =>
                     Iterator(objects(0).withFallback(objects(1)))
-                }
-                case n => {
+                case n =>
                     val leftSplits = for {
-                        i <- (1 until n)
+                        i <- 1 until n
                         pair = objects.splitAt(i)
                         first = pair._1.reduceLeft(_.withFallback(_))
                         second = pair._2.reduceLeft(_.withFallback(_))
                     } yield first.withFallback(second)
                     val rightSplits = for {
-                        i <- (1 until n)
+                        i <- 1 until n
                         pair = objects.splitAt(i)
                         first = pair._1.reduceRight(_.withFallback(_))
                         second = pair._2.reduceRight(_.withFallback(_))
                     } yield first.withFallback(second)
                     leftSplits.iterator ++ rightSplits.iterator
-                }
             }
         }
 
         val trees = makeTrees(allObjects).toSeq
         for (tree <- trees) {
             // if this fails, we were not associative.
-            if (!trees(0).equals(tree))
+            if (!trees.head.equals(tree))
                 throw new AssertionError("Merge was not associative, " +
                     "verify that it should not be, then don't use associativeMerge " +
                     "for this one. two results were: \none: " + trees(0) + "\ntwo: " +
@@ -349,7 +349,7 @@ class ConfigTest extends TestUtils {
         val fixUpCycle = parseObject(""" { "a" : { "b" : { "c" : 57 } } } """)
         val merged = mergeUnresolved(fixUpCycle, cycleObject)
         val v = resolveNoSystem(subst("foo"), merged)
-        assertEquals(intValue(57), v);
+        assertEquals(intValue(57), v)
     }
 
     @Test
@@ -399,7 +399,7 @@ class ConfigTest extends TestUtils {
             val resolved = resolveNoSystem(merged, merged)
 
             assertEquals(3, resolved.root.size())
-            assertEquals(42, resolved.getInt("j"));
+            assertEquals(42, resolved.getInt("j"))
             assertEquals(2, resolved.getInt("b.y"))
             assertEquals(3, resolved.getInt("c.z"))
         }
@@ -565,7 +565,7 @@ class ConfigTest extends TestUtils {
 
         // to get null we have to use the get() method from Map,
         // which takes a key and not a path
-        assertEquals(nullValue(), conf.getObject("nulls").get("null"))
+        assertEquals(nullValue, conf.getObject("nulls").get("null"))
         assertNull(conf.root.get("notinthefile"))
 
         // get stuff with getValue
@@ -811,6 +811,13 @@ class ConfigTest extends TestUtils {
         assertDurationAsTimeUnit(HOURS)
         assertDurationAsTimeUnit(DAYS)
 
+        // periods
+        assertEquals(1, conf.getPeriod("periods.day").get(ChronoUnit.DAYS))
+        assertEquals(2, conf.getPeriod("periods.dayAsNumber").getDays)
+        assertEquals(3 * 7, conf.getTemporal("periods.week").get(ChronoUnit.DAYS))
+        assertEquals(5, conf.getTemporal("periods.month").get(ChronoUnit.MONTHS))
+        assertEquals(8, conf.getTemporal("periods.year").get(ChronoUnit.YEARS))
+
         // should get size in bytes
         assertEquals(1024 * 1024L, conf.getBytes("memsizes.meg"))
         assertEquals(1024 * 1024L, conf.getBytes("memsizes.megAsNumber"))
@@ -941,8 +948,8 @@ class ConfigTest extends TestUtils {
         // include should have overridden the "ints" value in test03
         assertEquals(42, conf.getInt("test01.ints.fortyTwo"))
         // include should have been overridden by 42
-        assertEquals(42, conf.getInt("test01.booleans"));
-        assertEquals(42, conf.getInt("test01.booleans"));
+        assertEquals(42, conf.getInt("test01.booleans"))
+        assertEquals(42, conf.getInt("test01.booleans"))
         // include should have gotten .properties and .json also
         assertEquals("abc", conf.getString("test01.fromProps.abc"))
         assertEquals("A", conf.getString("test01.fromJsonA"))
@@ -973,10 +980,10 @@ class ConfigTest extends TestUtils {
 
         // check that includes into the root object work and that
         // "substitutions look relative-to-included-file first then at root second" works
-        assertEquals("This is in the included file", conf.getString("a"));
-        assertEquals("This is in the including file", conf.getString("b"));
-        assertEquals("This is in the included file", conf.getString("subtree.a"));
-        assertEquals("This is in the including file", conf.getString("subtree.b"));
+        assertEquals("This is in the included file", conf.getString("a"))
+        assertEquals("This is in the including file", conf.getString("b"))
+        assertEquals("This is in the included file", conf.getString("subtree.a"))
+        assertEquals("This is in the including file", conf.getString("subtree.b"))
     }
 
     @Test
@@ -1083,6 +1090,53 @@ class ConfigTest extends TestUtils {
         assertEquals(10, resolved.getInt("bar.nested.a.q"))
     }
 
+    @Test
+    def testEnvVariablesNameMangling() {
+        assertEquals("a", ConfigImplUtil.envVariableAsProperty("prefix_a", "prefix_"))
+        assertEquals("a.b", ConfigImplUtil.envVariableAsProperty("prefix_a_b", "prefix_"))
+        assertEquals("a.b.c", ConfigImplUtil.envVariableAsProperty("prefix_a_b_c", "prefix_"))
+        assertEquals("a.b-c-d", ConfigImplUtil.envVariableAsProperty("prefix_a_b__c__d", "prefix_"))
+        assertEquals("a.b_c_d", ConfigImplUtil.envVariableAsProperty("prefix_a_b___c___d", "prefix_"))
+
+        intercept[ConfigException.BadPath] {
+            ConfigImplUtil.envVariableAsProperty("prefix_____", "prefix_")
+        }
+        intercept[ConfigException.BadPath] {
+            ConfigImplUtil.envVariableAsProperty("prefix_a_b___c____d", "prefix_")
+        }
+    }
+
+    @Test
+    def testLoadWithEnvSubstitutions() {
+        System.setProperty("config.override_with_env_vars", "true")
+
+        try {
+            val loader02 = new TestClassLoader(this.getClass().getClassLoader(),
+                Map("reference.conf" -> resourceFile("test02.conf").toURI.toURL()))
+
+            val loader04 = new TestClassLoader(this.getClass().getClassLoader(),
+                Map("reference.conf" -> resourceFile("test04.conf").toURI.toURL()))
+
+            val conf02 = withContextClassLoader(loader02) {
+                ConfigFactory.load()
+            }
+
+            val conf04 = withContextClassLoader(loader04) {
+                ConfigFactory.load()
+            }
+
+            assertEquals(1, conf02.getInt("42_a"))
+            assertEquals(2, conf02.getInt("a.b.c"))
+            assertEquals(3, conf02.getInt("a-c"))
+            assertEquals(4, conf02.getInt("a_c"))
+
+            assertEquals("foo", conf04.getString("akka.version"))
+            assertEquals(10, conf04.getInt("akka.event-handler-dispatcher.max-pool-size"))
+        } finally {
+            System.clearProperty("config.override_with_env_vars")
+        }
+    }
+
     @Test
     def renderRoundTrip() {
         val allBooleans = true :: false :: Nil
@@ -1097,11 +1151,11 @@ class ConfigTest extends TestUtils {
                 .setOriginComments(originComments)
                 .setComments(comments)
                 .setJson(json)
-        }.toSeq
+        }
 
         for (i <- 1 to 10) {
             val numString = i.toString
-            val name = "/test" + { if (numString.size == 1) "0" else "" } + numString
+            val name = "/test" + { if (numString.length == 1) "0" else "" } + numString
             val conf = ConfigFactory.parseResourcesAnySyntax(classOf[ConfigTest], name,
                 ConfigParseOptions.defaults().setAllowMissing(false))
             for (renderOptions <- optionsCombos) {
@@ -1124,7 +1178,7 @@ class ConfigTest extends TestUtils {
                 if (renderOptions.getJson() && !(renderOptions.getComments() || renderOptions.getOriginComments())) {
                     // should get valid JSON if we don't have comments and are resolved
                     val json = try {
-                        ConfigFactory.parseString(resolvedRender, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON));
+                        ConfigFactory.parseString(resolvedRender, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
                     } catch {
                         case e: Exception =>
                             System.err.println("resolvedRender is not valid json: " + resolvedRender)
@@ -1233,4 +1287,70 @@ class ConfigTest extends TestUtils {
         val resolved = unresolved.resolveWith(source)
         assertEquals(43, resolved.getInt("foo"))
     }
+
+    /**
+     * A resolver that replaces paths that start with a particular prefix with
+     * strings where that prefix has been replaced with another prefix.
+     */
+    class DummyResolver(prefix: String, newPrefix: String, fallback: ConfigResolver) extends ConfigResolver {
+
+        override def lookup(path: String): ConfigValue = {
+            if (path.startsWith(prefix))
+                ConfigValueFactory.fromAnyRef(newPrefix + path.substring(prefix.length))
+            else if (fallback != null)
+                fallback.lookup(path)
+            else
+                null
+        }
+
+        override def withFallback(f: ConfigResolver): ConfigResolver = {
+            if (fallback == null)
+                new DummyResolver(prefix, newPrefix, f)
+            else
+                new DummyResolver(prefix, newPrefix, fallback.withFallback(f))
+        }
+
+    }
+
+    private def runFallbackTest(expected: String, source: String,
+        allowUnresolved: Boolean, resolvers: ConfigResolver*): Unit = {
+        val unresolved = ConfigFactory.parseString(source)
+        var options = ConfigResolveOptions.defaults().setAllowUnresolved(allowUnresolved)
+        for (resolver <- resolvers)
+            options = options.appendResolver(resolver)
+        val obj = unresolved.resolve(options).root()
+        assertEquals(expected, obj.render(ConfigRenderOptions.concise().setJson(false)))
+    }
+
+    @Test
+    def resolveFallback(): Unit = {
+        runFallbackTest(
+            "x=a,y=b",
+            "x=${a},y=${b}", false,
+            new DummyResolver("", "", null))
+        runFallbackTest(
+            "x=\"a.b.c\",y=\"a.b.d\"",
+            "x=${a.b.c},y=${a.b.d}", false,
+            new DummyResolver("", "", null))
+        runFallbackTest(
+            "x=${a.b.c},y=${a.b.d}",
+            "x=${a.b.c},y=${a.b.d}", true,
+            new DummyResolver("x.", "", null))
+        runFallbackTest(
+            "x=${a.b.c},y=\"e.f\"",
+            "x=${a.b.c},y=${d.e.f}", true,
+            new DummyResolver("d.", "", null))
+        runFallbackTest(
+            "w=\"Y.c.d\",x=${a},y=\"X.b\",z=\"Y.c\"",
+            "x=${a},y=${a.b},z=${a.b.c},w=${a.b.c.d}", true,
+            new DummyResolver("a.b.", "Y.", null),
+            new DummyResolver("a.", "X.", null))
+
+        runFallbackTest("x=${a.b.c}", "x=${a.b.c}", true, new DummyResolver("x.", "", null))
+        val e = intercept[ConfigException.UnresolvedSubstitution] {
+            runFallbackTest("x=${a.b.c}", "x=${a.b.c}", false, new DummyResolver("x.", "", null))
+        }
+        assertTrue(e.getMessage.contains("${a.b.c}"))
+    }
+
 }
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala
index 50add6786..8d2ec3846 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala
@@ -140,7 +140,7 @@ class ConfigValueTest extends TestUtils {
             "_z_e_d_C_o_n_f_i_g_V_a_l_u_e00000000000000010C0000_x_p_w_10200000025050000001906" +
             "0000000D000B_f_a_k_e_ _o_r_i_g_i_n090000000100010400000001000103000000010001_x"
 
-        val a = nullValue()
+        val a = nullValue
         val b = checkSerializable(expectedSerialization, a)
         assertNull("b is null", b.unwrapped)
     }
@@ -280,13 +280,13 @@ class ConfigValueTest extends TestUtils {
     }
 
     /**
-      * Reproduces the issue <a href=https://github.com/typesafehub/config/issues/461>#461</a>.
-      * <p>
-      * We use a custom de-/serializer that encodes String objects in a JDK-incompatible way. Encoding used here 
-      * is rather simplistic: a long indicating the length in bytes (JDK uses a variable length integer) followed
-      * by the string's bytes. Running this test with the original SerializedConfigValue.readExternal() 
-      * implementation results in an EOFException thrown during deserialization.
-      */
+     * Reproduces the issue <a href=https://github.com/lightbend/config/issues/461>#461</a>.
+     * <p>
+     * We use a custom de-/serializer that encodes String objects in a JDK-incompatible way. Encoding used here
+     * is rather simplistic: a long indicating the length in bytes (JDK uses a variable length integer) followed
+     * by the string's bytes. Running this test with the original SerializedConfigValue.readExternal()
+     * implementation results in an EOFException thrown during deserialization.
+     */
     @Test
     def configConfigCustomSerializable() {
         val aMap = configMap("a" -> 1, "b" -> 2, "c" -> 3)
@@ -295,7 +295,7 @@ class ConfigValueTest extends TestUtils {
 
         assertEquals(expected, actual)
     }
-    
+
     @Test
     def configListEquality() {
         val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
@@ -424,11 +424,11 @@ class ConfigValueTest extends TestUtils {
         longValue(11).toString()
         doubleValue(3.14).toString()
         stringValue("hi").toString()
-        nullValue().toString()
+        nullValue.toString()
         boolValue(true).toString()
         val emptyObj = SimpleConfigObject.empty()
         emptyObj.toString()
-        (new SimpleConfigList(fakeOrigin(), Collections.emptyList[AbstractConfigValue]())).toString()
+        new SimpleConfigList(fakeOrigin(), Collections.emptyList[AbstractConfigValue]()).toString()
         subst("a").toString()
         substInString("b").toString()
         val dm = new ConfigDelayedMerge(fakeOrigin(), List[AbstractConfigValue](subst("a"), subst("b")).asJava)
@@ -499,7 +499,7 @@ class ConfigValueTest extends TestUtils {
         val l: ConfigList = new SimpleConfigList(fakeOrigin(),
             scalaSeq.asJava)
 
-        assertEquals(scalaSeq(0), l.get(0))
+        assertEquals(scalaSeq.head, l.get(0))
         assertEquals(scalaSeq(1), l.get(1))
         assertEquals(scalaSeq(2), l.get(2))
 
@@ -510,7 +510,7 @@ class ConfigValueTest extends TestUtils {
 
         assertEquals(1, l.indexOf(scalaSeq(1)))
 
-        assertFalse(l.isEmpty());
+        assertFalse(l.isEmpty())
 
         assertEquals(scalaSeq, l.iterator().asScala.toSeq)
 
@@ -611,12 +611,12 @@ class ConfigValueTest extends TestUtils {
 
         val obj = parseConfig("{ a : " + a + ", b : " + b + ", c : " + c + ", d : " + d + "}")
         assertEquals(Seq(a, b, c, d),
-            Seq("a", "b", "c", "d") map { obj.getString(_) })
+            Seq("a", "b", "c", "d") map { obj.getString })
 
         // make sure it still works if we're doing concatenation
         val obj2 = parseConfig("{ a : xx " + a + " yy, b : xx " + b + " yy, c : xx " + c + " yy, d : xx " + d + " yy}")
         assertEquals(Seq(a, b, c, d) map { "xx " + _ + " yy" },
-            Seq("a", "b", "c", "d") map { obj2.getString(_) })
+            Seq("a", "b", "c", "d") map { obj2.getString })
     }
 
     @Test
@@ -625,25 +625,25 @@ class ConfigValueTest extends TestUtils {
             val values = new java.util.HashMap[String, AbstractConfigValue]()
             if (!empty)
                 values.put("hello", intValue(37))
-            new SimpleConfigObject(SimpleConfigOrigin.newSimple(desc), values);
+            new SimpleConfigObject(SimpleConfigOrigin.newSimple(desc), values)
         }
         def m(values: AbstractConfigObject*) = {
             AbstractConfigObject.mergeOrigins(values: _*).description()
         }
 
         // simplest case
-        assertEquals("merge of a,b", m(o("a", false), o("b", false)))
+        assertEquals("merge of a,b", m(o("a", empty = false), o("b", empty = false)))
         // combine duplicate "merge of"
-        assertEquals("merge of a,x,y", m(o("a", false), o("merge of x,y", false)))
-        assertEquals("merge of a,b,x,y", m(o("merge of a,b", false), o("merge of x,y", false)))
+        assertEquals("merge of a,x,y", m(o("a", empty = false), o("merge of x,y", empty = false)))
+        assertEquals("merge of a,b,x,y", m(o("merge of a,b", empty = false), o("merge of x,y", empty = false)))
         // ignore empty objects
-        assertEquals("a", m(o("foo", true), o("a", false)))
+        assertEquals("a", m(o("foo", empty = true), o("a", empty = false)))
         // unless they are all empty, pick the first one
-        assertEquals("foo", m(o("foo", true), o("a", true)))
+        assertEquals("foo", m(o("foo", empty = true), o("a", empty = true)))
         // merge just one
-        assertEquals("foo", m(o("foo", false)))
+        assertEquals("foo", m(o("foo", empty = false)))
         // merge three
-        assertEquals("merge of a,b,c", m(o("a", false), o("b", false), o("c", false)))
+        assertEquals("merge of a,b,c", m(o("a", empty = false), o("b", empty = false), o("c", empty = false)))
     }
 
     @Test
@@ -661,7 +661,7 @@ class ConfigValueTest extends TestUtils {
         assertTrue(obj.hasPath("b"))
 
         // hasPath() is false for null values but containsKey is true
-        assertEquals(nullValue(), obj.root.get("a"))
+        assertEquals(nullValue, obj.root.get("a"))
         assertTrue(obj.root.containsKey("a"))
         assertFalse(obj.hasPath("a"))
 
@@ -729,7 +729,7 @@ class ConfigValueTest extends TestUtils {
         assertEquals(-1, noFilename.lineNumber())
 
         assertEquals("foo: 3", filenameWithLine.description())
-        assertEquals("bar: 4", noFilenameWithLine.description());
+        assertEquals("bar: 4", noFilenameWithLine.description())
 
         assertEquals(3, filenameWithLine.lineNumber())
         assertEquals(4, noFilenameWithLine.lineNumber())
@@ -944,10 +944,10 @@ class ConfigValueTest extends TestUtils {
         })
         def top(v: SimpleConfigList) = v.origin
         def middle(v: SimpleConfigList) = v.get(0).origin
-        def bottom(v: SimpleConfigList) = if (v.get(0).isInstanceOf[ConfigList])
-            Some(v.get(0).asInstanceOf[ConfigList].get(0).origin)
-        else
-            None
+        def bottom(v: SimpleConfigList) = v.get(0) match {
+            case list: ConfigList => Some(list.get(0).origin)
+            case _ => None
+        }
 
         //System.err.println("values=\n  " + values.map(v => top(v).description + ", " + middle(v).description + ", " + bottom(v).map(_.description)).mkString("\n  "))
         for (v <- values) {
@@ -982,8 +982,8 @@ class ConfigValueTest extends TestUtils {
 
     @Test
     def renderSorting(): Unit = {
-        val config = parseConfig("""0=a,1=b,2=c,3=d,10=e,20=f,30=g""")
+        val config = parseConfig("""0=a,1=b,2=c,999999999999999999999999999999999999999999999=0,3=d,10=e,20a=f,20=g,30=h""")
         val rendered = config.root.render(ConfigRenderOptions.concise())
-        assertEquals("""{"0":"a","1":"b","2":"c","3":"d","10":"e","20":"f","30":"g"}""", rendered)
+        assertEquals("""{"0":"a","1":"b","2":"c","3":"d","10":"e","20":"g","30":"h","999999999999999999999999999999999999999999999":0,"20a":"f"}""", rendered)
     }
 }
diff --git a/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala b/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala
index 3dd6d9938..62176cfe2 100644
--- a/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala
@@ -63,7 +63,7 @@ class EquivalentsTest extends TestUtils {
             val (originals, others) = files.partition({ f => f.getName().startsWith("original.") })
             if (originals.isEmpty)
                 throw new RuntimeException("Need a file named 'original' in " + equiv.getPath())
-            if (originals.size > 1)
+            if (originals.length > 1)
                 throw new RuntimeException("Multiple 'original' files in " + equiv.getPath() + ": " + originals)
             val original = parse(originals(0))
 
diff --git a/config/src/test/scala/com/typesafe/config/impl/HttpTest.scala b/config/src/test/scala/com/typesafe/config/impl/HttpTest.scala
index 4cd56f064..bc8cb7e59 100644
--- a/config/src/test/scala/com/typesafe/config/impl/HttpTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/HttpTest.scala
@@ -92,7 +92,7 @@ object HttpTest {
 
     private var server: Option[ToyHttp] = None
 
-    def port = server.map(_.port).getOrElse(throw new Exception("http server isn't running"))
+    def port: Int = server.map(_.port).getOrElse(throw new Exception("http server isn't running"))
     def baseUrl = s"http://127.0.0.1:$port"
 
     private def handleThreeTypes(request: Request, json: String, props: String, hocon: String): Response = {
diff --git a/config/src/test/scala/com/typesafe/config/impl/JsonTest.scala b/config/src/test/scala/com/typesafe/config/impl/JsonTest.scala
index 6ba421f74..c9a2a1170 100644
--- a/config/src/test/scala/com/typesafe/config/impl/JsonTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/JsonTest.scala
@@ -3,29 +3,28 @@
  */
 package com.typesafe.config.impl
 
-import org.junit.Assert._
-import org.junit._
-import net.liftweb.{ json => lift }
 import java.io.Reader
-import java.io.StringReader
+import java.util
+
 import com.typesafe.config._
-import java.util.HashMap
-import java.util.Collections
+import net.liftweb.{ json => lift }
+import org.junit.Assert._
+import org.junit._
 
 class JsonTest extends TestUtils {
 
     def parse(s: String): ConfigValue = {
         val options = ConfigParseOptions.defaults().
             setOriginDescription("test json string").
-            setSyntax(ConfigSyntax.JSON);
-        Parseable.newString(s, options).parseValue();
+            setSyntax(ConfigSyntax.JSON)
+        Parseable.newString(s, options).parseValue()
     }
 
     def parseAsConf(s: String): ConfigValue = {
         val options = ConfigParseOptions.defaults().
             setOriginDescription("test conf string").
-            setSyntax(ConfigSyntax.CONF);
-        Parseable.newString(s, options).parseValue();
+            setSyntax(ConfigSyntax.CONF)
+        Parseable.newString(s, options).parseValue()
     }
 
     private[this] def toLift(value: ConfigValue): lift.JValue = {
@@ -56,13 +55,11 @@ class JsonTest extends TestUtils {
 
         liftValue match {
             case lift.JObject(fields) =>
-                val m = new HashMap[String, AbstractConfigValue]()
+                val m = new util.HashMap[String, AbstractConfigValue]()
                 fields.foreach({ field => m.put(field.name, fromLift(field.value)) })
                 new SimpleConfigObject(fakeOrigin(), m)
             case lift.JArray(values) =>
-                new SimpleConfigList(fakeOrigin(), values.map(fromLift(_)).asJava)
-            case lift.JField(name, value) =>
-                throw new IllegalStateException("either JField was a toplevel from lift-json or this function is buggy")
+                new SimpleConfigList(fakeOrigin(), values.map(fromLift).asJava)
             case lift.JInt(i) =>
                 if (i.isValidInt) intValue(i.intValue) else longValue(i.longValue)
             case lift.JBool(b) =>
@@ -147,7 +144,7 @@ class JsonTest extends TestUtils {
         var tested = 0
 
         // be sure we do the same thing as Lift when we build our JSON "DOM"
-        for (valid <- whitespaceVariations(validJson, true)) {
+        for (valid <- whitespaceVariations(validJson, validInLift = true)) {
             val liftAST = if (valid.liftBehaviorUnexpected) {
                 SimpleConfigObject.empty()
             } else {
diff --git a/config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala b/config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala
new file mode 100644
index 000000000..3ded66625
--- /dev/null
+++ b/config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala
@@ -0,0 +1,30 @@
+package com.typesafe.config.impl
+
+import java.io.InputStreamReader
+
+import com.typesafe.config.{ ConfigException, ConfigFactory, ConfigParseOptions }
+import org.hamcrest.CoreMatchers.containsString
+import org.junit.Assert.{ assertEquals, assertThat }
+import org.junit.Test
+
+class ParseableReaderTest extends TestUtils {
+
+    @Test
+    def parse(): Unit = {
+        val filename = "/test01.properties"
+        val configInput = new InputStreamReader(getClass.getResourceAsStream(filename))
+        val config = ConfigFactory.parseReader(configInput, ConfigParseOptions.defaults()
+            .setSyntaxFromFilename(filename))
+        assertEquals("hello^^", config.getString("fromProps.specialChars"))
+    }
+
+    @Test
+    def parseIncorrectFormat(): Unit = {
+        val filename = "/test01.properties"
+        val configInput = new InputStreamReader(getClass.getResourceAsStream(filename))
+        val e = intercept[ConfigException.Parse] {
+            ConfigFactory.parseReader(configInput)
+        }
+        assertThat(e.getMessage, containsString("Expecting end of input or a comma, got '^'"))
+    }
+}
diff --git a/config/src/test/scala/com/typesafe/config/impl/PathTest.scala b/config/src/test/scala/com/typesafe/config/impl/PathTest.scala
index 20a4d626c..9c53a9303 100644
--- a/config/src/test/scala/com/typesafe/config/impl/PathTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/PathTest.scala
@@ -15,13 +15,13 @@ class PathTest extends TestUtils {
         // note: foo.bar is a single key here
         val a = Path.newKey("foo.bar")
         // check that newKey worked
-        assertEquals(path("foo.bar"), a);
+        assertEquals(path("foo.bar"), a)
         val sameAsA = Path.newKey("foo.bar")
         val differentKey = Path.newKey("hello")
         // here foo.bar is two elements
         val twoElements = Path.newPath("foo.bar")
         // check that newPath worked
-        assertEquals(path("foo", "bar"), twoElements);
+        assertEquals(path("foo", "bar"), twoElements)
         val sameAsTwoElements = Path.newPath("foo.bar")
 
         checkEqualObjects(a, a)
diff --git a/config/src/test/scala/com/typesafe/config/impl/PropertiesTest.scala b/config/src/test/scala/com/typesafe/config/impl/PropertiesTest.scala
index 75cfe0fec..b20278e47 100644
--- a/config/src/test/scala/com/typesafe/config/impl/PropertiesTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/PropertiesTest.scala
@@ -108,7 +108,7 @@ class PropertiesTest extends TestUtils {
 
         val conf = ConfigFactory.parseProperties(props, ConfigParseOptions.defaults())
         val reference = ConfigFactory.parseString("{ a : [0,1,2,3,4] }")
-        assertEquals(Seq(0, 1, 2, 3, 4), conf.getIntList("a").asScala.toSeq)
+        assertEquals(Seq(0, 1, 2, 3, 4), conf.getIntList("a").asScala)
         conf.checkValid(reference)
     }
 
diff --git a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
index 7d63026f2..1643a4a31 100644
--- a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
@@ -653,6 +653,26 @@ class PublicApiTest extends TestUtils {
         }
     }
 
+    @Test
+    def loadEnvironmentVariablesOverridesIfConfigured(): Unit = {
+        assertEquals("config.override_with_env_vars is not set", null, System.getProperty("config.override_with_env_vars"))
+
+        System.setProperty("config.override_with_env_vars", "true")
+
+        try {
+            val loaderB2 = new TestClassLoader(this.getClass().getClassLoader(),
+                Map("reference.conf" -> resourceFile("b_2.conf").toURI.toURL()))
+
+            val configB2 = withContextClassLoader(loaderB2) {
+                ConfigFactory.load()
+            }
+
+            assertEquals(5, configB2.getInt("b"))
+        } finally {
+            System.clearProperty("config.override_with_env_vars")
+        }
+    }
+
     @Test
     def usesContextClassLoaderForApplicationConf() {
         val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(),
@@ -906,7 +926,7 @@ class PublicApiTest extends TestUtils {
     }
 
     // We would ideally make this case NOT throw an exception but we need to do some work
-    // to get there, see https://github.com/typesafehub/config/issues/160
+    // to get there, see https://github.com/lightbend/config/issues/160
     @Test
     def detectIncludeFromList() {
         val e = intercept[ConfigException.Parse] {
@@ -1070,7 +1090,7 @@ class PublicApiTest extends TestUtils {
 
     @Test
     def heuristicIncludeChecksClasspath(): Unit = {
-        // from https://github.com/typesafehub/config/issues/188
+        // from https://github.com/lightbend/config/issues/188
         withScratchDirectory("heuristicIncludeChecksClasspath") { dir =>
             val f = new File(dir, "foo.conf")
             writeFile(f, """
@@ -1132,6 +1152,28 @@ include "onclasspath"
         // missing underneath missing
         intercept[ConfigException.Missing] { conf.getIsNull("x.c.y") }
     }
+
+    @Test
+    def applicationConfCanOverrideReferenceConf(): Unit = {
+        val loader = new TestClassLoader(this.getClass.getClassLoader,
+            Map(
+                "reference.conf" -> resourceFile("test13-reference-with-substitutions.conf").toURI.toURL,
+                "application.conf" -> resourceFile("test13-application-override-substitutions.conf").toURI.toURL))
+
+        assertEquals("b", ConfigFactory.defaultReference(loader).getString("a"))
+        assertEquals("overridden", ConfigFactory.load(loader).getString("a"))
+    }
+
+    @Test(expected = classOf[ConfigException.UnresolvedSubstitution])
+    def referenceConfMustResolveIndependently(): Unit = {
+        val loader = new TestClassLoader(this.getClass.getClassLoader,
+            Map(
+                "reference.conf" -> resourceFile("test13-reference-bad-substitutions.conf").toURI.toURL,
+                "application.conf" -> resourceFile("test13-application-override-substitutions.conf").toURI.toURL))
+
+        ConfigFactory.load(loader)
+    }
+
 }
 
 class TestStrategy extends DefaultConfigLoadingStrategy {
@@ -1145,4 +1187,4 @@ object TestStrategy {
     private var invocations = 0
     def getIncovations() = invocations
     def increment() = invocations += 1
-}
\ No newline at end of file
+}
diff --git a/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala b/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala
index 13ccd1600..d33e215fe 100644
--- a/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala
@@ -249,7 +249,7 @@ abstract trait TestUtils {
                     "possibly caused by http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627",
                     nf)
             case e: Exception =>
-                System.err.println(e.getStackTrace.toString);
+                e.printStackTrace(System.err)
                 throw new AssertionError("failed to make a copy via serialization", e)
         }
 
@@ -551,7 +551,7 @@ abstract trait TestUtils {
             { s: String => s.replace(" ", "") }, // this would break with whitespace in a key or value
             { s: String => s.replace(":", " : ") }, // could break with : in a key or value
             { s: String => s.replace(",", " , ") } // could break with , in a key or value
-            )
+        )
         tests flatMap { t =>
             if (t.whitespaceMatters) {
                 Seq(t)
@@ -574,7 +574,7 @@ abstract trait TestUtils {
     protected def intValue(i: Int) = new ConfigInt(fakeOrigin(), i, null)
     protected def longValue(l: Long) = new ConfigLong(fakeOrigin(), l, null)
     protected def boolValue(b: Boolean) = new ConfigBoolean(fakeOrigin(), b)
-    protected def nullValue() = new ConfigNull(fakeOrigin())
+    protected def nullValue = new ConfigNull(fakeOrigin())
     protected def stringValue(s: String) = new ConfigString.Quoted(fakeOrigin(), s)
     protected def doubleValue(d: Double) = new ConfigDouble(fakeOrigin(), d, null)
 
diff --git a/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala b/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala
index 96afa1664..79750bc83 100644
--- a/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala
@@ -198,12 +198,12 @@ class TokenizerTest extends TestUtils {
             """\"\""", // file ends with a backslash
             "$", // file ends with a $
             "${" // file ends with a ${
-            )
+        )
 
         for (t <- invalidTests) {
             val tokenized = tokenizeAsList(t)
-            val maybeProblem = tokenized.find(Tokens.isProblem(_))
-            assertTrue(s"expected failure for <$t> but got ${t}", maybeProblem.isDefined)
+            val maybeProblem = tokenized.find(Tokens.isProblem)
+            assertTrue(s"expected failure for <$t> but got $t", maybeProblem.isDefined)
         }
     }
 
@@ -247,9 +247,9 @@ class TokenizerTest extends TestUtils {
         abstract class NumberTest(val s: String, val result: Token)
         case class LongTest(override val s: String, override val result: Token) extends NumberTest(s, result)
         case class DoubleTest(override val s: String, override val result: Token) extends NumberTest(s, result)
-        implicit def pair2inttest(pair: (String, Int)) = LongTest(pair._1, tokenLong(pair._2))
-        implicit def pair2longtest(pair: (String, Long)) = LongTest(pair._1, tokenLong(pair._2))
-        implicit def pair2doubletest(pair: (String, Double)) = DoubleTest(pair._1, tokenDouble(pair._2))
+        implicit def pair2inttest(pair: (String, Int)): LongTest = LongTest(pair._1, tokenLong(pair._2))
+        implicit def pair2longtest(pair: (String, Long)): LongTest = LongTest(pair._1, tokenLong(pair._2))
+        implicit def pair2doubletest(pair: (String, Double)): DoubleTest = DoubleTest(pair._1, tokenDouble(pair._2))
 
         val tests = List[NumberTest](("1", 1),
             ("1.2", 1.2),
@@ -302,7 +302,7 @@ class TokenizerTest extends TestUtils {
         for (invalid <- "+`^?!@*&\\") {
             val tokenized = tokenizeAsList(invalid.toString)
             assertEquals(3, tokenized.size)
-            assertEquals(Tokens.START, tokenized(0))
+            assertEquals(Tokens.START, tokenized.head)
             assertEquals(Tokens.END, tokenized(2))
             val problem = tokenized(1)
             assertTrue("reserved char is a problem", Tokens.isProblem(problem))
diff --git a/config/src/test/scala/com/typesafe/config/impl/ToyHttp.scala b/config/src/test/scala/com/typesafe/config/impl/ToyHttp.scala
index 0bf39c2c0..70214b435 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ToyHttp.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ToyHttp.scala
@@ -25,10 +25,7 @@ final class ToyHttp(handler: ToyHttp.Request => ToyHttp.Response) {
     private final val serverSocket = new ServerSocket()
     serverSocket.bind(new InetSocketAddress("127.0.0.1", 0))
     final val port = serverSocket.getLocalPort
-    private final val thread = new Thread(new Runnable() {
-        override def run() =
-            mainLoop();
-    })
+    private final val thread = new Thread(() => mainLoop())
 
     thread.setDaemon(true)
     thread.setName("ToyHttp")
@@ -118,8 +115,8 @@ final class ToyHttp(handler: ToyHttp.Request => ToyHttp.Response) {
         //val stuff = new java.io.ByteArrayOutputStream
         //val writer = new PrintWriter(new OutputStreamWriter(stuff, StandardCharsets.UTF_8))
         val writer = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))
-        val dateFormat = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
-        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+        val dateFormat = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US)
+        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"))
 
         writer.append(s"HTTP/1.1 ${response.code} ${codeText(response.code)}\r\n")
         writer.append(s"Date: ${dateFormat.format(new Date)}\r\n")
diff --git a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala
index b4ee9a1ba..0a7f7e3e3 100644
--- a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala
@@ -3,6 +3,9 @@
  */
 package com.typesafe.config.impl
 
+import java.time.{ LocalDate, Period }
+import java.time.temporal.ChronoUnit
+
 import org.junit.Assert._
 import org.junit._
 import com.typesafe.config._
@@ -40,7 +43,34 @@ class UnitParserTest extends TestUtils {
         assertTrue(e2.getMessage.contains("duration number"))
     }
 
-    // https://github.com/typesafehub/config/issues/117
+    @Test
+    def parsePeriod(): Unit = {
+        val oneYears = List(
+            "1y", "1 y", "1year", "1 years", "   1y   ", "   1   y    ",
+            "365", "365d", "365 d", "365 days", "   365   days   ", "365day",
+            "12m", "12mo", "12 m", "   12   mo   ", "12 months", "12month")
+        val epochDate = LocalDate.ofEpochDay(0)
+        val oneYear = ChronoUnit.DAYS.between(epochDate, epochDate.plus(Period.ofYears(1)))
+        for (y <- oneYears) {
+            val period = SimpleConfig.parsePeriod(y, fakeOrigin(), "test")
+            val dayCount = ChronoUnit.DAYS.between(epochDate, epochDate.plus(period))
+            assertEquals(oneYear, dayCount)
+        }
+
+        // bad units
+        val e = intercept[ConfigException.BadValue] {
+            SimpleConfig.parsePeriod("100 dollars", fakeOrigin(), "test")
+        }
+        assertTrue(s"${e.getMessage} was not the expected error message", e.getMessage.contains("time unit"))
+
+        // bad number
+        val e2 = intercept[ConfigException.BadValue] {
+            SimpleConfig.parsePeriod("1 00 seconds", fakeOrigin(), "test")
+        }
+        assertTrue(s"${e2.getMessage} was not the expected error message", e2.getMessage.contains("time unit 'seconds'"))
+    }
+
+    // https://github.com/lightbend/config/issues/117
     // this broke because "1d" is a valid double for parseDouble
     @Test
     def parseOneDayAsMilliseconds(): Unit = {
@@ -127,7 +157,7 @@ class UnitParserTest extends TestUtils {
     @Test
     def parseHugeMemorySizes(): Unit = {
         def parseMem(s: String): Long = SimpleConfig.parseBytes(s, fakeOrigin(), "test")
-        def assertOutOfRange(s: String) = {
+        def assertOutOfRange(s: String): Unit = {
             val fail = intercept[ConfigException.BadValue] {
                 parseMem(s)
             }
diff --git a/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala b/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala
index cf31a7166..6d0bb160c 100644
--- a/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala
@@ -3,6 +3,7 @@
  */
 package com.typesafe.config.impl
 
+import com.typesafe.config.ConfigSyntax
 import org.junit.Assert._
 import org.junit._
 
@@ -57,7 +58,7 @@ class UtilTest extends TestUtils {
         assertTrue(ConfigImplUtil.equalsHandlingNull("", ""))
     }
 
-    val lotsOfStrings = (invalidJson ++ validConf).map(_.test)
+    val lotsOfStrings: List[String] = (invalidJson ++ validConf).map(_.test)
 
     private def roundtripJson(s: String) {
         val rendered = ConfigImplUtil.renderJsonString(s)
@@ -90,4 +91,29 @@ class UtilTest extends TestUtils {
             roundtripUnquoted(s)
         }
     }
+
+    @Test
+    def syntaxFromExtensionConf(): Unit = {
+        assertEquals(ConfigSyntax.CONF, ConfigImplUtil.syntaxFromExtension("application.conf"))
+    }
+
+    @Test
+    def syntaxFromExtensionJson(): Unit = {
+        assertEquals(ConfigSyntax.JSON, ConfigImplUtil.syntaxFromExtension("application.json"))
+    }
+
+    @Test
+    def syntaxFromExtensionProperties(): Unit = {
+        assertEquals(ConfigSyntax.PROPERTIES, ConfigImplUtil.syntaxFromExtension("application.properties"))
+    }
+
+    @Test
+    def syntaxFromExtensionUnknown(): Unit = {
+        assertNull(ConfigImplUtil.syntaxFromExtension("application.exe"))
+    }
+
+    @Test
+    def syntaxFromExtensionNull(): Unit = {
+        assertNull(ConfigImplUtil.syntaxFromExtension(null))
+    }
 }
diff --git a/config/src/test/scala/com/typesafe/config/impl/ValidationTest.scala b/config/src/test/scala/com/typesafe/config/impl/ValidationTest.scala
index 6dce7bfee..5e45aad61 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ValidationTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ValidationTest.scala
@@ -97,6 +97,21 @@ class ValidationTest extends TestUtils {
         checkValidationException(e, expecteds)
     }
 
+    @Test
+    def validationFailedSerializable(): Unit = {
+        // Reusing a previous test case to generate an error
+        val reference = parseConfig("""{ a : [{},{},{}] }""")
+        val conf = parseConfig("""{ a : 42 }""")
+        val e = intercept[ConfigException.ValidationFailed] {
+            conf.checkValid(reference)
+        }
+
+        val expecteds = Seq(WrongType("a", 1, "list", "number"))
+
+        val actual = checkSerializableNoMeaningfulEquals(e)
+        checkValidationException(actual, expecteds)
+    }
+
     @Test
     def validationAllowsListOverriddenWithSameTypeList() {
         val reference = parseConfig("""{ a : [1,2,3] }""")
diff --git a/examples/scala/complex-app/src/main/scala/ComplexApp.scala b/examples/scala/complex-app/src/main/scala/ComplexApp.scala
index 507da8cb1..68d530303 100644
--- a/examples/scala/complex-app/src/main/scala/ComplexApp.scala
+++ b/examples/scala/complex-app/src/main/scala/ComplexApp.scala
@@ -35,7 +35,7 @@ object ComplexApp extends App {
     //////////
 
     // "config2" shows how to configure a library with a custom settings subtree
-    val config2 = ConfigFactory.load("complex2");
+    val config2 = ConfigFactory.load("complex2")
 
     // use the config ourselves
     println("config2, complex-app.something=" + config2.getString("complex-app.something"))
diff --git a/examples/scala/simple-lib/src/main/scala/simplelib/SimpleLib.scala b/examples/scala/simple-lib/src/main/scala/simplelib/SimpleLib.scala
index 06b03c0a5..5c02c0920 100644
--- a/examples/scala/simple-lib/src/main/scala/simplelib/SimpleLib.scala
+++ b/examples/scala/simple-lib/src/main/scala/simplelib/SimpleLib.scala
@@ -40,9 +40,9 @@ class SimpleLibSettings(config: Config) {
 
     // note that these fields are NOT lazy, because if we're going to
     // get any exceptions, we want to get them on startup.
-    val foo = config.getString("simple-lib.foo")
-    val hello = config.getString("simple-lib.hello")
-    val whatever = config.getString("simple-lib.whatever")
+    val foo: String = config.getString("simple-lib.foo")
+    val hello: String = config.getString("simple-lib.hello")
+    val whatever: String = config.getString("simple-lib.whatever")
 }
 
 // This is a different way to do SimpleLibContext, using the
diff --git a/project/Build.scala b/project/Build.scala
deleted file mode 100644
index 00d33767c..000000000
--- a/project/Build.scala
+++ /dev/null
@@ -1,119 +0,0 @@
-import sbt._
-import Keys._
-import com.etsy.sbt.checkstyle.CheckstylePlugin.autoImport._
-import com.typesafe.sbt.osgi.SbtOsgi
-import com.typesafe.sbt.osgi.SbtOsgi.autoImport._
-import com.typesafe.sbt.JavaVersionCheckPlugin.autoImport._
-
-object ConfigBuild extends Build {
-    val unpublished = Seq(
-        // no artifacts in this project
-        publishArtifact := false,
-        // make-pom has a more specific publishArtifact setting already
-        // so needs specific override
-        publishArtifact in makePom := false,
-        // no docs to publish
-        publishArtifact in packageDoc := false,
-        // can't seem to get rid of ivy files except by no-op'ing the entire publish task
-        publish := {},
-        publishLocal := {}
-    )
-
-    object sonatype extends PublishToSonatype {
-        def projectUrl    = "https://github.com/typesafehub/config"
-        def developerId   = "havocp"
-        def developerName = "Havoc Pennington"
-        def developerUrl  = "http://ometer.com/"
-        def scmUrl        = "git://github.com/typesafehub/config.git"
-    }
-
-    override val settings = super.settings ++ Seq(isSnapshot <<= isSnapshot or version(_ endsWith "-SNAPSHOT"))
-
-    lazy val commonSettings: Seq[Setting[_]] = unpublished ++ Seq(javaVersionPrefix in javaVersionCheck := None)
-
-    lazy val rootSettings: Seq[Setting[_]] =
-      commonSettings ++
-      Seq(aggregate in doc := false,
-          doc := (doc in (configLib, Compile)).value,
-          aggregate in packageDoc := false,
-          packageDoc := (packageDoc in (configLib, Compile)).value,
-          aggregate in checkstyle := false,
-          checkstyle := (checkstyle in (configLib, Compile)).value)
-
-    lazy val root = Project(id = "root",
-                            base = file("."),
-                            settings = rootSettings) aggregate(testLib, configLib,
-                                                               simpleLibScala, simpleAppScala, complexAppScala,
-                                                               simpleLibJava, simpleAppJava, complexAppJava)
-
-    lazy val configLib = Project(id = "config",
-                                 base = file("config"),
-                                 settings =
-                                   sonatype.settings ++
-                                   osgiSettings ++
-                                   Seq(
-                                     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")
-                                   )).enablePlugins(SbtOsgi) dependsOn testLib % "test->test"
-
-    def project(id: String, base: File) = Project(id, base, settings = commonSettings)
-
-    lazy val testLib = project("config-test-lib", file("test-lib"))
-
-    lazy val simpleLibScala  = project("config-simple-lib-scala",  file("examples/scala/simple-lib"))  dependsOn configLib
-    lazy val simpleAppScala  = project("config-simple-app-scala",  file("examples/scala/simple-app"))  dependsOn simpleLibScala
-    lazy val complexAppScala = project("config-complex-app-scala", file("examples/scala/complex-app")) dependsOn simpleLibScala
-
-    lazy val simpleLibJava  = project("config-simple-lib-java",  file("examples/java/simple-lib"))  dependsOn configLib
-    lazy val simpleAppJava  = project("config-simple-app-java",  file("examples/java/simple-app"))  dependsOn simpleLibJava
-    lazy val complexAppJava = project("config-complex-app-java", file("examples/java/complex-app")) dependsOn simpleLibJava
-}
-
-// from https://raw.github.com/paulp/scala-improving/master/project/PublishToSonatype.scala
-
-abstract class PublishToSonatype {
-  val ossSnapshots = "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
-  val ossStaging   = "Sonatype OSS Staging" at "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
-
-  def projectUrl: String
-  def developerId: String
-  def developerName: String
-  def developerUrl: String
-
-  def licenseName         = "Apache License, Version 2.0"
-  def licenseUrl          = "http://www.apache.org/licenses/LICENSE-2.0"
-  def licenseDistribution = "repo"
-  def scmUrl: String
-  def scmConnection       = "scm:git:" + scmUrl
-
-  def generatePomExtra: xml.NodeSeq = {
-    <url>{ projectUrl }</url>
-      <licenses>
-        <license>
-          <name>{ licenseName }</name>
-          <url>{ licenseUrl }</url>
-          <distribution>{ licenseDistribution }</distribution>
-        </license>
-      </licenses>
-    <scm>
-      <url>{ scmUrl }</url>
-      <connection>{ scmConnection }</connection>
-    </scm>
-    <developers>
-      <developer>
-        <id>{ developerId }</id>
-        <name>{ developerName }</name>
-        <url>{ developerUrl }</url>
-      </developer>
-    </developers>
-  }
-
-  def settings: Seq[Setting[_]] = Seq(
-    publishMavenStyle := true,
-    publishTo <<= isSnapshot { (snapshot) => Some(if (snapshot) ossSnapshots else ossStaging) },
-    publishArtifact in Test := false,
-    pomIncludeRepository := (_ => false),
-    pomExtra := generatePomExtra
-  )
-}
diff --git a/project/build.properties b/project/build.properties
index 43b8278c6..c0bab0494 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.11
+sbt.version=1.2.8
diff --git a/project/linksource.scala b/project/linksource.scala
index 01c3226de..7e98040fb 100644
--- a/project/linksource.scala
+++ b/project/linksource.scala
@@ -1,4 +1,4 @@
-import sbt._
+import sbt.{Def, _}
 import Keys._
 import plugins.JvmPlugin
 
@@ -12,7 +12,7 @@ object LinkSourcePlugin extends AutoPlugin {
 
   override def trigger = allRequirements
   override def requires = JvmPlugin
-  override lazy val projectSettings = Seq(
+  override lazy val projectSettings: Seq[Def.Setting[_ >: Option[String] with Task[Seq[String]] with Task[File] <: Product]] = Seq(
     javadocSourceBaseUrl := None,
     javacOptions in (Compile, doc) := {
       val old = (javacOptions in doc).value
@@ -26,18 +26,19 @@ object LinkSourcePlugin extends AutoPlugin {
 
       val dir = (target in doc in Compile).value
 
-      javadocSourceBaseUrl.value.foreach { url =>
-        rewriteSourceLinks(dir, url, streams.value.log)
+      (javadocSourceBaseUrl.value, streams.value) match {
+        case (Some(url), streamz) =>
+          rewriteSourceLinks(dir, url, streamz.log)
+        case _ =>
       }
 
       result
     }
   )
 
-
   private def rewriteSourceLinks(dir: File, sourceBaseUrl: String, log: Logger): Unit = {
     // Convert <a href="../../../src-html/com/typesafe/config/Config.html#line.165"> to
-    // "https://github.com/typesafehub/config/blob/v1.2.1/config/src/main/java/com/typesafe/config/Config.java#L165"
+    // "https://github.com/lightbend/config/blob/v1.2.1/config/src/main/java/com/typesafe/config/Config.java#L165"
     // in all .html files found underneath dir
     val origRegex = "href=\".*src-html/([^\"]+)\"".r
     def listFiles(d: File): Seq[File] = IO.listFiles(d).toSeq.flatMap { f =>
diff --git a/project/plugins.sbt b/project/plugins.sbt
index de21c2d70..38196f0ac 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,10 +1,10 @@
-addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.4.0")
-addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.6")
-addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
-addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.8.0")
-addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
+addSbtPlugin("com.github.sbt" % "sbt-findbugs" % "2.0.0")
+addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "3.1.0")
+addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.0-M2")
+addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.3")
+addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")
 
-addSbtPlugin("com.etsy" % "sbt-checkstyle-plugin" % "3.0.0")
+addSbtPlugin("com.etsy" % "sbt-checkstyle-plugin" % "3.1.1")
 
-addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5")
-addSbtPlugin("com.typesafe.sbt" % "sbt-javaversioncheck" % "0.1.0")
+addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0")
+addSbtPlugin("com.eed3si9n" % "sbt-nocomma" % "0.1.0")