From aa75211b52d0a92dddb6973a4b8b1ea174568941 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 3 Aug 2017 10:43:54 +0100 Subject: [PATCH 01/83] Migrate to sbt 0.13.16 --- build.sbt | 65 +++++++++++++++++ config/build.sbt | 2 +- project/Build.scala | 119 -------------------------------- project/PublishToSonatype.scala | 48 +++++++++++++ project/build.properties | 2 +- 5 files changed, 115 insertions(+), 121 deletions(-) delete mode 100644 project/Build.scala create mode 100644 project/PublishToSonatype.scala diff --git a/build.sbt b/build.sbt index 65be9256f..1eba6413b 100644 --- a/build.sbt +++ b/build.sbt @@ -13,6 +13,58 @@ scalacOptions in GlobalScope in Test := Seq("-unchecked", "-deprecation", "-feat scalaVersion in ThisBuild := "2.10.4" +val sonatype = new 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" +} + +lazy val commonSettings: Seq[Setting[_]] = Def.settings( + unpublished, + javaVersionPrefix in javaVersionCheck := None +) + +lazy val root = (project in file(".")) + .settings( + commonSettings, + 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 + ) + .aggregate( + testLib, configLib, + simpleLibScala, simpleAppScala, complexAppScala, + simpleLibJava, simpleAppJava, complexAppJava + ) + +lazy val configLib = Project("config", file("config")) + .settings( + sonatype.settings, + osgiSettings, + 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 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 + useGpg := true aggregate in PgpKeys.publishSigned := false @@ -20,3 +72,16 @@ PgpKeys.publishSigned := (PgpKeys.publishSigned in configLib).value aggregate in PgpKeys.publishLocalSigned := false PgpKeys.publishLocalSigned := (PgpKeys.publishLocalSigned in configLib).value + +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 := {} +) diff --git a/config/build.sbt b/config/build.sbt index 7d3fbe218..5249ee598 100644 --- a/config/build.sbt +++ b/config/build.sbt @@ -62,7 +62,7 @@ checkstyle in Compile := { } // add checkstyle as a dependency of doc -doc in Compile <<= (doc in Compile).dependsOn(checkstyle in Compile) +doc in Compile := ((doc in Compile).dependsOn(checkstyle in Compile)).value findbugsSettings findbugsReportType := Some(ReportType.Html) 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 = { - { projectUrl } - - - { licenseName } - { licenseUrl } - { licenseDistribution } - - - - { scmUrl } - { scmConnection } - - - - { developerId } - { developerName } - { developerUrl } - - - } - - 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/PublishToSonatype.scala b/project/PublishToSonatype.scala new file mode 100644 index 000000000..b6b7ca546 --- /dev/null +++ b/project/PublishToSonatype.scala @@ -0,0 +1,48 @@ +import sbt._, Keys._ + +// 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 = { + { projectUrl } + + + { licenseName } + { licenseUrl } + { licenseDistribution } + + + + { scmUrl } + { scmConnection } + + + + { developerId } + { developerName } + { developerUrl } + + + } + + def settings: Seq[Setting[_]] = Seq( + publishMavenStyle := true, + publishTo := Some(if (isSnapshot.value) ossSnapshots else ossStaging), + publishArtifact in Test := false, + pomIncludeRepository := (_ => false), + pomExtra := generatePomExtra + ) +} diff --git a/project/build.properties b/project/build.properties index 43b8278c6..c091b86ca 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.11 +sbt.version=0.13.16 From 64e0a5e07d5b3ccd8dc4064e969499975dd1e0e8 Mon Sep 17 00:00:00 2001 From: Karthick Sankarachary Date: Fri, 15 Sep 2017 18:13:33 -0700 Subject: [PATCH 02/83] #495 Add Support For Set Types In Config Beans --- .../typesafe/config/impl/ConfigBeanImpl.java | 8 + .../src/test/java/beanconfig/SetsConfig.java | 139 ++++++++++++++++++ .../resources/beanconfig/beanconfig01.conf | 26 ++++ .../config/impl/ConfigBeanFactoryTest.scala | 33 +++++ 4 files changed, 206 insertions(+) create mode 100644 config/src/test/java/beanconfig/SetsConfig.java 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..66d059ba1 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]; diff --git a/config/src/test/java/beanconfig/SetsConfig.java b/config/src/test/java/beanconfig/SetsConfig.java new file mode 100644 index 000000000..b81540df9 --- /dev/null +++ b/config/src/test/java/beanconfig/SetsConfig.java @@ -0,0 +1,139 @@ +package beanconfig; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigMemorySize; +import com.typesafe.config.ConfigObject; +import com.typesafe.config.ConfigValue; + +import java.time.Duration; +import java.util.Set; + +public class SetsConfig { + + Set empty; + Set ofInt; + Set ofString; + Set ofDouble; + Set ofLong; + Set ofNull; + Set ofBoolean; + Set ofObject; + Set ofConfig; + Set ofConfigObject; + Set ofConfigValue; + Set ofDuration; + Set ofMemorySize; + Set ofStringBean; + + public Set getEmpty() { + return empty; + } + + public void setEmpty(Set empty) { + this.empty = empty; + } + + public Set getOfInt() { + return ofInt; + } + + public void setOfInt(Set ofInt) { + this.ofInt = ofInt; + } + + public Set getOfString() { + return ofString; + } + + public void setOfString(Set ofString) { + this.ofString = ofString; + } + + public Set getOfDouble() { + return ofDouble; + } + + public void setOfDouble(Set ofDouble) { + this.ofDouble = ofDouble; + } + + public Set getOfNull() { + return ofNull; + } + + public void setOfNull(Set ofNull) { + this.ofNull = ofNull; + } + + public Set getOfBoolean() { + return ofBoolean; + } + + public void setOfBoolean(Set ofBoolean) { + this.ofBoolean = ofBoolean; + } + + public Set getOfObject() { + return ofObject; + } + + public void setOfObject(Set ofObject) { + this.ofObject = ofObject; + } + + public Set getOfLong() { + return ofLong; + } + + public void setOfLong(Set ofLong) { + this.ofLong = ofLong; + } + + public Set getOfConfig() { + return ofConfig; + } + + public void setOfConfig(Set ofConfig) { + this.ofConfig = ofConfig; + } + + public Set getOfConfigObject() { + return ofConfigObject; + } + + public void setOfConfigObject(Set ofConfigObject) { + this.ofConfigObject = ofConfigObject; + } + + public Set getOfConfigValue() { + return ofConfigValue; + } + + public void setOfConfigValue(Set ofConfigValue) { + this.ofConfigValue = ofConfigValue; + } + + public Set getOfDuration() { + return ofDuration; + } + + public void setOfDuration(Set ofDuration) { + this.ofDuration = ofDuration; + } + + public Set getOfMemorySize() { + return ofMemorySize; + } + + public void setOfMemorySize(Set ofMemorySize) { + this.ofMemorySize = ofMemorySize; + } + + public Set getOfStringBean() { + return ofStringBean; + } + + public void setOfStringBean(Set 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/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala index 8c85e4550..00e5a093c 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala @@ -132,6 +132,39 @@ class ConfigBeanFactoryTest extends TestUtils { 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]) From 0a5603fe3854739f5a813bfe94107b75c87fb4e8 Mon Sep 17 00:00:00 2001 From: Christian Plesner Hansen Date: Mon, 25 Sep 2017 20:09:58 +0200 Subject: [PATCH 03/83] Add fallback ConfigReferenceResolver --- .../typesafe/config/ConfigResolveOptions.java | 77 ++++++++++++++++++- .../com/typesafe/config/ConfigResolver.java | 38 +++++++++ .../typesafe/config/impl/ConfigReference.java | 5 +- .../com/typesafe/config/impl/ConfigTest.scala | 66 ++++++++++++++++ 4 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 config/src/main/java/com/typesafe/config/ConfigResolver.java diff --git a/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java b/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java index 2b7140416..96e0eca0a 100644 --- a/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java +++ b/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java @@ -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, + * + *
+     *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
+     *         .appendResolver(primary)
+     *         .appendResolver(secondary)
+     *         .appendResolver(tertiary);
+     * 
+ * + * 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/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 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/test/scala/com/typesafe/config/impl/ConfigTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala index d88b365c4..96b55835d 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -1233,4 +1233,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*) = { + 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}")) + } + } From 4ee9f1829ab1d2c557413d712336fc3eee4b2175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Fri, 6 Oct 2017 16:10:14 +0300 Subject: [PATCH 04/83] Release notes for 1.3.2 --- NEWS.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/NEWS.md b/NEWS.md index 22d4cfb65..697903208 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,17 @@ +# 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/typesafehub/config/milestone/1?closed=1). + # 1.3.1: September 24, 2016 - added `include required("foo")` syntax to specify includes that From 032dc701012cba6785faa1a2dd3c50c7f5cf21fc Mon Sep 17 00:00:00 2001 From: Ian Tabolt Date: Fri, 6 Oct 2017 13:50:26 -0400 Subject: [PATCH 05/83] Add test case for serializing ValidationFailed --- .../com/typesafe/config/impl/ValidationTest.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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] }""") From 73dd3c815ac4b46d7b1e3b76543b23632767587f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?z=20i=20=CE=BB=20a=20s=20a=20l?= Date: Tue, 17 Oct 2017 11:16:34 +0200 Subject: [PATCH 06/83] Add C# port to Other APIs section --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b291be549..c408e75d7 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ to merge it in. - [Python port](#python-port) - [C++ port](#c-port) - [JavaScript port](#javascript-port) + - [C# port](#csharp-port) - [Linting tool](#linting-tool) @@ -880,6 +881,10 @@ format. #### JavaScript port * https://github.com/yellowblood/hocon-js (missing features, under development) + +#### C# port + + * https://github.com/akkadotnet/HOCON #### Linting tool From 3e30fc711df5da627b0c749d128abfa351a940e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?z=20i=20=CE=BB=20a=20s=20a=20l?= Date: Tue, 17 Oct 2017 11:18:37 +0200 Subject: [PATCH 07/83] update anchor --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c408e75d7..388ad1cf2 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ to merge it in. - [Python port](#python-port) - [C++ port](#c-port) - [JavaScript port](#javascript-port) - - [C# port](#csharp-port) + - [C# port](#c-port-1) - [Linting tool](#linting-tool) From b077d272fb1b00895fa0a7641067b17717f0ada1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Mon, 23 Oct 2017 13:40:51 +0300 Subject: [PATCH 08/83] Rename typesafehub to lightbend (#513) * Migration to lightbend repo * Second round of the rename cleanup --- NEWS.md | 6 +-- README.md | 44 +++++++++---------- build.sbt | 4 +- config/build.sbt | 2 +- .../main/java/com/typesafe/config/Config.java | 20 ++++----- .../com/typesafe/config/ConfigFactory.java | 4 +- .../com/typesafe/config/ConfigMergeable.java | 2 +- .../typesafe/config/ConfigResolveOptions.java | 2 +- .../com/typesafe/config/ConfigSyntax.java | 4 +- .../typesafe/config/impl/ConfigParser.java | 4 +- .../com/typesafe/config/impl/package.html | 2 +- .../java/com/typesafe/config/package.html | 18 ++++---- .../typesafe/config/parser/ConfigNode.java | 4 +- .../com/typesafe/config/parser/package.html | 2 +- .../config/impl/ConcatenationTest.scala | 12 ++--- .../config/impl/ConfigValueTest.scala | 2 +- .../typesafe/config/impl/PublicApiTest.scala | 4 +- .../typesafe/config/impl/UnitParserTest.scala | 2 +- project/linksource.scala | 2 +- 19 files changed, 70 insertions(+), 70 deletions(-) diff --git a/NEWS.md b/NEWS.md index 697903208..60280909f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,7 @@ 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/typesafehub/config/milestone/1?closed=1). + in the [milestone page](https://github.com/lightbend/config/milestone/1?closed=1). # 1.3.1: September 24, 2016 @@ -222,7 +222,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/ + http://lightbend.github.io/config/latest/api/ # 1.1.0-9f31d6308e7ebbc3d7904b64ebb9f61f7e22a968: January 6, 2014 @@ -240,7 +240,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 388ad1cf2..c154cce10 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 @@ -123,15 +123,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: http://lightbend.github.com/config/latest/api/ - also published in jar form - consider reading this README first for an intro - for questions about the `.conf` file format, read - [HOCON.md](https://github.com/typesafehub/config/blob/master/HOCON.md) + [HOCON.md](https://github.com/lightbend/config/blob/master/HOCON.md) in this directory ### Bugs and Patches @@ -145,7 +145,7 @@ account - it takes 30 seconds. You can do this at http://www.typesafe.com/contribute/cla Please see -[CONTRIBUTING](https://github.com/typesafehub/config/blob/master/CONTRIBUTING.md) +[CONTRIBUTING](https://github.com/lightbend/config/blob/master/CONTRIBUTING.md) for more including how to make a release. ### Build @@ -166,7 +166,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`. @@ -190,7 +190,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](http://lightbend.github.com/config/latest/api/) for details of course. ### Schemas and Validation @@ -199,7 +199,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](http://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) @@ -436,7 +436,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](http://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: @@ -451,7 +451,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 @@ -464,7 +464,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](http://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 @@ -476,15 +476,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](http://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()](http://lightbend.github.io/config/latest/api/com/typesafe/config/Config.html#root%28%29) and -[ConfigObject.toConfig()](http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigObject.html#toConfig%28%29). +[ConfigObject.toConfig()](http://lightbend.github.io/config/latest/api/com/typesafe/config/ConfigObject.html#toConfig%28%29). ### ConfigBeanFactory @@ -507,7 +507,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 @@ -772,7 +772,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. @@ -854,10 +854,10 @@ format. #### 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) @@ -879,13 +879,13 @@ 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 - + #### Linting tool * A web based linting tool http://www.hoconlint.com/ diff --git a/build.sbt b/build.sbt index 1eba6413b..dd5bb77f8 100644 --- a/build.sbt +++ b/build.sbt @@ -14,11 +14,11 @@ scalacOptions in GlobalScope in Test := Seq("-unchecked", "-deprecation", "-feat scalaVersion in ThisBuild := "2.10.4" val sonatype = new PublishToSonatype { - def projectUrl = "https://github.com/typesafehub/config" + def projectUrl = "https://github.com/lightbend/config" def developerId = "havocp" def developerName = "Havoc Pennington" def developerUrl = "http://ometer.com/" - def scmUrl = "git://github.com/typesafehub/config.git" + def scmUrl = "git://github.com/lightbend/config.git" } lazy val commonSettings: Seq[Setting[_]] = Def.settings( diff --git a/config/build.sbt b/config/build.sbt index 5249ee598..13c074615 100644 --- a/config/build.sbt +++ b/config/build.sbt @@ -84,7 +84,7 @@ javacOptions in (Compile, doc) ++= Seq("-group", s"Public API (version ${version javadocSourceBaseUrl := { for (gitHead <- com.typesafe.sbt.SbtGit.GitKeys.gitHeadCommit.value) - yield s"https://github.com/typesafehub/config/blob/$gitHead/config/src/main/java" + yield s"https://github.com/lightbend/config/blob/$gitHead/config/src/main/java" } javaVersionPrefix in javaVersionCheck := Some("1.8") diff --git a/config/src/main/java/com/typesafe/config/Config.java b/config/src/main/java/com/typesafe/config/Config.java index 68a36e784..d9aea4c4b 100644 --- a/config/src/main/java/com/typesafe/config/Config.java +++ b/config/src/main/java/com/typesafe/config/Config.java @@ -36,7 +36,7 @@ * *

* You can find an example app and library on + * href="https://github.com/lightbend/config/tree/master/examples">on * GitHub. Also be sure to read the package overview which * describes the big picture as shown in those examples. @@ -56,7 +56,7 @@ * in a JSON object; it's just a string that's the key in a map. A "path" is a * parseable expression with a syntax and it refers to a series of keys. Path * expressions are described in the spec for + * href="https://github.com/lightbend/config/blob/master/HOCON.md">spec for * Human-Optimized Config Object Notation. In brief, a path is * period-separated so "a.b.c" looks for key c in object b in object a in the * root object. Sometimes double quotes are needed around special characters in @@ -110,7 +110,7 @@ *

* Substitutions are the ${foo.bar} syntax in config * files, described in the specification. Resolving substitutions replaces these references with real * values. * @@ -189,7 +189,7 @@ public interface Config extends ConfigMergeable { /** * Returns a replacement config with all substitutions (the * ${foo.bar} syntax, see the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec) resolved. Substitutions are looked up using this * Config as the root object, that is, a substitution * ${foo.bar} will be replaced with the result of @@ -670,7 +670,7 @@ public interface Config extends ConfigMergeable { * the value is already a number, then it's left alone; if it's a string, * it's parsed understanding unit suffixes such as "128K", as documented in * the the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec. * * @param path @@ -690,7 +690,7 @@ public interface Config extends ConfigMergeable { * the value is already a number, then it's left alone; if it's a string, * it's parsed understanding unit suffixes such as "128K", as documented in * the the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec. * * @since 1.3.0 @@ -711,7 +711,7 @@ public interface Config extends ConfigMergeable { * Get value as a duration in milliseconds. If the value is already a * number, then it's left alone; if it's a string, it's parsed understanding * units suffixes like "10m" or "5ns" as documented in the the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec. * * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} @@ -754,7 +754,7 @@ public interface Config extends ConfigMergeable { * number, then it's taken as milliseconds and then converted to the * requested TimeUnit; if it's a string, it's parsed understanding units * suffixes like "10m" or "5ns" as documented in the the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec. * * @since 1.2.0 @@ -778,7 +778,7 @@ public interface Config extends ConfigMergeable { * already a number, then it's taken as milliseconds; if it's * a string, it's parsed understanding units suffixes like * "10m" or "5ns" as documented in the the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec. This method never returns null. * * @since 1.3.0 @@ -800,7 +800,7 @@ public interface Config extends ConfigMergeable { * already a number, then it's taken as days; if it's * a string, it's parsed understanding units suffixes like * "10d" or "5w" as documented in the the + * href="https://github.com/lightbend/config/blob/master/HOCON.md">the * spec. This method never returns null. * * @since 1.3.0 diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java index 7938dc918..86d995e36 100644 --- a/config/src/main/java/com/typesafe/config/ConfigFactory.java +++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java @@ -28,7 +28,7 @@ * from a resource and nothing else. * *

You can find an example app and library on + * href="https://github.com/lightbend/config/tree/master/examples">on * GitHub. Also be sure to read the package * overview which describes the big picture as shown in those @@ -570,7 +570,7 @@ public static Config systemEnvironment() { /** * Converts a Java {@link java.util.Properties} object to a * {@link ConfigObject} using the rules documented in the HOCON + * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON * spec. The keys in the Properties object are split on the * period character '.' and treated as paths. The values will all end up as * string values. If you have both "a=foo" and "a.b=bar" in your properties diff --git a/config/src/main/java/com/typesafe/config/ConfigMergeable.java b/config/src/main/java/com/typesafe/config/ConfigMergeable.java index 7af36f7bf..58b2663e8 100644 --- a/config/src/main/java/com/typesafe/config/ConfigMergeable.java +++ b/config/src/main/java/com/typesafe/config/ConfigMergeable.java @@ -27,7 +27,7 @@ public interface ConfigMergeable { * *

* The semantics of merging are described in the spec + * href="https://github.com/lightbend/config/blob/master/HOCON.md">spec * for HOCON. Merging typically occurs when either the same object is * created twice in the same file, or two config files are both loaded. For * example: diff --git a/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java b/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java index 96e0eca0a..526da1eff 100644 --- a/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java +++ b/config/src/main/java/com/typesafe/config/ConfigResolveOptions.java @@ -6,7 +6,7 @@ /** * A set of options related to resolving substitutions. Substitutions use the * ${foo.bar} syntax and are documented in the HOCON + * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON * spec. *

* Typically this class would be used with the method 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 (JSON, HOCON + * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON * aka ".conf", or Java properties). @@ -21,7 +21,7 @@ public enum ConfigSyntax { JSON, /** * The JSON-superset HOCON format. Associated with the .conf file extension * and application/hocon Content-Type. */ 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..cce541636 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigParser.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigParser.java @@ -192,7 +192,7 @@ private void parseInclude(Map 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/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 impl has any guarantees; it will change whenever someone feels like changing it. If you feel you need access to something -in impl, please +in impl, please file a feature request.

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 @@

-An API for loading and using configuration files, see the project site +An API for loading and using configuration files, see the project site for more information.

Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use it with methods in the {@link com.typesafe.config.Config} interface. Configuration may be in the form of JSON files, -Java properties, or HOCON files; you may also +Java properties, or HOCON files; you may also build your own configuration in code or from your own file formats.

@@ -26,8 +26,8 @@ If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()} there's no need to pass a configuration to your libraries and frameworks, as long as they all default to this same default, which they should. -
Example application code: Java and Scala. -
Showing a couple of more special-purpose features, a more complex example: Java and Scala. +
Example application code: Java and Scala. +
Showing a couple of more special-purpose features, a more complex example: Java and Scala.

@@ -36,21 +36,21 @@ call {@link com.typesafe.config.ConfigFactory#load()} to get the default one. Typically a library might offer two constructors, one with a Config parameter and one which uses {@link com.typesafe.config.ConfigFactory#load()}. -
Example library code: Java and Scala. +
Example library code: Java and Scala.

-Check out the full examples directory on GitHub. +Check out the full examples directory on GitHub.

What else to read:

  • The overview documentation for interface {@link com.typesafe.config.Config}.
  • -
  • The README for the library.
  • +
  • The README for the library.
  • If you want to use .conf files in addition to .json and .properties, - see the README for some short examples - and the full HOCON spec for the long version.
  • + see the README for some short examples + and the full HOCON spec for the long version.

diff --git a/config/src/main/java/com/typesafe/config/parser/ConfigNode.java b/config/src/main/java/com/typesafe/config/parser/ConfigNode.java index 43ac3f51b..e9954e9a7 100644 --- a/config/src/main/java/com/typesafe/config/parser/ConfigNode.java +++ b/config/src/main/java/com/typesafe/config/parser/ConfigNode.java @@ -10,8 +10,8 @@ * Note: at present there is no way to obtain an instance of this interface, so * please ignore it. A future release will make syntax tree nodes available in * the public API. If you are interested in working on it, please see: https://github.com/typesafehub/config/issues/300 + * href="https://github.com/lightbend/config/issues/300" + * >https://github.com/lightbend/config/issues/300 * *

* Because this object is immutable, it is safe to use from multiple threads and diff --git a/config/src/main/java/com/typesafe/config/parser/package.html b/config/src/main/java/com/typesafe/config/parser/package.html index ef099cc4d..b0e7b3cea 100644 --- a/config/src/main/java/com/typesafe/config/parser/package.html +++ b/config/src/main/java/com/typesafe/config/parser/package.html @@ -14,7 +14,7 @@ the com.typesafe.config package instead. You would use the raw parser if you're doing something like reading, modifying, and re-saving a config file. For info on the main config API this parser is a part of, -see the project site. +see the project site.

diff --git a/config/src/test/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/ConfigValueTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala index 50add6786..9d1a49a45 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala @@ -280,7 +280,7 @@ class ConfigValueTest extends TestUtils { } /** - * Reproduces the issue #461. + * Reproduces the issue #461. *

* 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 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..d25d43659 100644 --- a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala @@ -906,7 +906,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 +1070,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, """ 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 f03593d17..a06a40c19 100644 --- a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala @@ -70,7 +70,7 @@ class UnitParserTest extends TestUtils { assertTrue(s"${e2.getMessage} was not the expected error message", e2.getMessage.contains("time unit 'seconds'")) } - // https://github.com/typesafehub/config/issues/117 + // https://github.com/lightbend/config/issues/117 // this broke because "1d" is a valid double for parseDouble @Test def parseOneDayAsMilliseconds(): Unit = { diff --git a/project/linksource.scala b/project/linksource.scala index 01c3226de..3c1834974 100644 --- a/project/linksource.scala +++ b/project/linksource.scala @@ -37,7 +37,7 @@ object LinkSourcePlugin extends AutoPlugin { private def rewriteSourceLinks(dir: File, sourceBaseUrl: String, log: Logger): Unit = { // Convert 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 => From 408925b2c83d30776f2aaa1af9947baf1f379c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Mon, 23 Oct 2017 13:55:02 +0300 Subject: [PATCH 09/83] Some more link fixes after the rename (#517) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c154cce10..4f4cf9f27 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ https://github.com/lightbend/config/blob/master/NEWS.md ### API docs - - Online: http://lightbend.github.com/config/latest/api/ + - Online: http://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 @@ -190,7 +190,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://lightbend.github.com/config/latest/api/) for +[API docs](http://lightbend.github.io/config/latest/api/) for details of course. ### Schemas and Validation From 98b4b0835dd4e997b41fa19bc6ac04cdcbfbf9e0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 24 Oct 2017 18:00:53 +0900 Subject: [PATCH 10/83] Align maintanance note with other lightbend/ projects --- README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4f4cf9f27..ccb915b6a 100644 --- a/README.md +++ b/README.md @@ -95,10 +95,6 @@ to merge it in. ## 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 @@ -889,3 +885,15 @@ format. #### Linting tool * A web based linting tool http://www.hoconlint.com/ + +# Maintanance notes + +## License + +The license is Apache 2.0, see LICENSE-2.0.txt. + +## Maintained by + +This project is maintained mostly by @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! From a9e9075449aa93569be2bfb11666a096e61d9041 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 24 Oct 2017 18:02:45 +0900 Subject: [PATCH 11/83] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ccb915b6a..2c0a0c742 100644 --- a/README.md +++ b/README.md @@ -894,6 +894,6 @@ The license is Apache 2.0, see LICENSE-2.0.txt. ## Maintained by -This project is maintained mostly by @havocp and [@akka-team](https://github.com/orgs/lightbend/teams/akka-team/members). +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! From 10e576fb7726c0b4395d4f181558c3d745911349 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sun, 5 Nov 2017 10:32:59 +0100 Subject: [PATCH 12/83] Add circe-config to list of Scala wrappers --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2c0a0c742..1ce689c9c 100644 --- a/README.md +++ b/README.md @@ -846,6 +846,7 @@ 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 #### Clojure wrappers for the Java library From b25446e261bd95b41c2baf5197f83c601386745e Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Mon, 6 Nov 2017 03:28:43 +0100 Subject: [PATCH 13/83] Update links to use HTTPS (#526) --- CONTRIBUTING.md | 2 +- HOCON.md | 6 +++--- LICENSE-2.0.txt | 4 ++-- NEWS.md | 2 +- README.md | 18 +++++++++--------- config/src/test/resources/test04.conf | 2 +- project/PublishToSonatype.scala | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3227a333..30ebc7fd8 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 diff --git a/HOCON.md b/HOCON.md index f85c4b050..6e0818c05 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 @@ -1337,7 +1337,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. @@ -1427,7 +1427,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 60280909f..0e93dee25 100644 --- a/NEWS.md +++ b/NEWS.md @@ -222,7 +222,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://lightbend.github.io/config/latest/api/ + https://lightbend.github.io/config/latest/api/ # 1.1.0-9f31d6308e7ebbc3d7904b64ebb9f61f7e22a968: January 6, 2014 diff --git a/README.md b/README.md index 1ce689c9c..220ae87f3 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ https://github.com/lightbend/config/blob/master/NEWS.md ### API docs - - Online: http://lightbend.github.io/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 @@ -138,7 +138,7 @@ 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/lightbend/config/blob/master/CONTRIBUTING.md) @@ -186,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://lightbend.github.io/config/latest/api/) for +[API docs](https://lightbend.github.io/config/latest/api/) for details of course. ### Schemas and Validation @@ -195,7 +195,7 @@ There isn't a schema language or anything like that. However, two suggested tools are: - use the - [checkValid() method](http://lightbend.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) @@ -432,7 +432,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://lightbend.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: @@ -460,7 +460,7 @@ skip `null` values. You can also look at a `Config` in the way most JSON APIs would, through the -[ConfigObject](http://lightbend.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 @@ -472,15 +472,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://lightbend.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://lightbend.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://lightbend.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 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/project/PublishToSonatype.scala b/project/PublishToSonatype.scala index b6b7ca546..9c12d5cd9 100644 --- a/project/PublishToSonatype.scala +++ b/project/PublishToSonatype.scala @@ -11,7 +11,7 @@ abstract class PublishToSonatype { def developerUrl: String def licenseName = "Apache License, Version 2.0" - def licenseUrl = "http://www.apache.org/licenses/LICENSE-2.0" + def licenseUrl = "https://www.apache.org/licenses/LICENSE-2.0" def licenseDistribution = "repo" def scmUrl: String def scmConnection = "scm:git:" + scmUrl From d284ee5ff5a7100fb9f13239eba68ae0715d505e Mon Sep 17 00:00:00 2001 From: James Roper Date: Wed, 21 Feb 2018 15:10:20 +1100 Subject: [PATCH 14/83] Publish with namespaced JDK9 module name (#547) Fixes #546. Adds Automatic-Module-Name: typesafe.config, rather than defaulting to the very generic module name of config that the JVM selects. --- build.sbt | 4 +++- project/plugins.sbt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index dd5bb77f8..c501afe43 100644 --- a/build.sbt +++ b/build.sbt @@ -48,7 +48,9 @@ lazy val configLib = Project("config", file("config")) osgiSettings, 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") + publishLocal := sys.error("use publishLocalSigned instead of plain publishLocal"), + packageOptions in (Compile, packageBin) += + Package.ManifestAttributes("Automatic-Module-Name" -> "typesafe.config" ) ) .enablePlugins(SbtOsgi) .dependsOn(testLib % "test->test") diff --git a/project/plugins.sbt b/project/plugins.sbt index de21c2d70..75b78fbee 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,7 @@ 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-osgi" % "0.9.3") addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1") addSbtPlugin("com.etsy" % "sbt-checkstyle-plugin" % "3.0.0") From 39e52a77d3424e32610d060d962d9c4bc4c9a846 Mon Sep 17 00:00:00 2001 From: fabio fumarola Date: Wed, 21 Feb 2018 05:11:56 +0100 Subject: [PATCH 15/83] Update README.md (#545) update version to 1.3.2 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 220ae87f3..cf4bed1ff 100644 --- a/README.md +++ b/README.md @@ -105,12 +105,12 @@ You can find published releases on Maven Central. com.typesafe config - 1.3.1 + 1.3.2 sbt dependency: - libraryDependencies += "com.typesafe" % "config" % "1.3.1" + libraryDependencies += "com.typesafe" % "config" % "1.3.2" Link for direct download if you don't use a dependency manager: From 173a783f30667440eb9edb94cd7c62d31f27d62c Mon Sep 17 00:00:00 2001 From: Siyavash Habashi Date: Wed, 21 Feb 2018 05:56:31 +0100 Subject: [PATCH 16/83] Update versions to 1.3.2 (#534) From 17e0f65d13455e8ca2cfcde4259f6a6742e2a1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Brb?= Date: Wed, 21 Feb 2018 08:55:34 -0500 Subject: [PATCH 17/83] Never pass null ConfigOrigin to PathParser.parsePathNodeExpression (#538) This was causing NPEs on certain parse errors while trying to construct a ConfigException --- .../java/com/typesafe/config/impl/ConfigDocumentParser.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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)); } } From 424bad149c800b5a8c3e7a4981b244f43b54baf9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 22 Feb 2018 00:27:35 +0900 Subject: [PATCH 18/83] news for 1.3.3 --- NEWS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.md b/NEWS.md index 0e93dee25..12fd05e14 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# 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 From 3eedbed02db1ac914fd232a4ef66a6d34bc27ba9 Mon Sep 17 00:00:00 2001 From: Sathiya Date: Fri, 2 Mar 2018 17:09:48 +0100 Subject: [PATCH 19/83] Adds trace while allowing missing file by returing empty parse value --- config/src/main/java/com/typesafe/config/impl/Parseable.java | 2 ++ 1 file changed, 2 insertions(+) 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..ae54a7d42 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() From 7637d777d018311ee7f961abb8d3d43afb797435 Mon Sep 17 00:00:00 2001 From: av_elier Date: Tue, 12 Jun 2018 23:30:38 +0300 Subject: [PATCH 20/83] Add playground link to readme --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf4bed1ff..18b617b36 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ to merge it in. - [JavaScript port](#javascript-port) - [C# port](#c-port-1) - [Linting tool](#linting-tool) + - [Online playground](#online-playground) @@ -886,7 +887,11 @@ format. #### Linting tool * A web based linting tool http://www.hoconlint.com/ - + +#### Online playground + + * https://hocon-playground.herokuapp.com/ + # Maintanance notes ## License From 9f71f62290da830ba3c2e66f4900ffbf07c9acc9 Mon Sep 17 00:00:00 2001 From: Enno Runne <458526+ennru@users.noreply.github.com> Date: Sat, 30 Jun 2018 19:21:49 +0200 Subject: [PATCH 21/83] upgrade to sbt 1.1.6 and latest plugins --- .travis.yml | 8 +------- build.sbt | 3 +-- config/build.sbt | 16 +++------------- project/build.properties | 2 +- project/linksource.scala | 7 ++++--- project/plugins.sbt | 13 ++++++------- 6 files changed, 16 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index d957beebf..9c62ad817 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,13 +4,7 @@ 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 + - $HOME/.sbt/boot language: scala diff --git a/build.sbt b/build.sbt index c501afe43..026af3434 100644 --- a/build.sbt +++ b/build.sbt @@ -22,8 +22,7 @@ val sonatype = new PublishToSonatype { } lazy val commonSettings: Seq[Setting[_]] = Def.settings( - unpublished, - javaVersionPrefix in javaVersionCheck := None + unpublished ) lazy val root = (project in file(".")) diff --git a/config/build.sbt b/config/build.sbt index 13c074615..8b5c49e76 100644 --- a/config/build.sbt +++ b/config/build.sbt @@ -1,12 +1,7 @@ -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) @@ -64,15 +59,12 @@ checkstyle in Compile := { // add checkstyle as a dependency of doc doc in Compile := ((doc in Compile).dependsOn(checkstyle in Compile)).value -findbugsSettings -findbugsReportType := Some(ReportType.Html) +findbugsReportType := Some(FindbugsReport.Html) findbugsReportPath := Some(crossTarget.value / "findbugs.html") -findbugsEffort := Effort.Maximum +findbugsEffort := FindbugsEffort.Maximum findbugsMaxMemory := 2000 -jacoco.settings - -javacOptions in (Compile, compile) ++= Seq("-source", "1.6", "-target", "1.8", +javacOptions in (Compile, compile) ++= Seq("-source", "1.8", "-target", "1.8", "-g", "-Xlint:unchecked") // because we test some global state such as singleton caches, @@ -86,5 +78,3 @@ javadocSourceBaseUrl := { for (gitHead <- com.typesafe.sbt.SbtGit.GitKeys.gitHeadCommit.value) yield s"https://github.com/lightbend/config/blob/$gitHead/config/src/main/java" } - -javaVersionPrefix in javaVersionCheck := Some("1.8") diff --git a/project/build.properties b/project/build.properties index c091b86ca..d6e35076c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.16 +sbt.version=1.1.6 diff --git a/project/linksource.scala b/project/linksource.scala index 3c1834974..70f49fc0d 100644 --- a/project/linksource.scala +++ b/project/linksource.scala @@ -26,15 +26,16 @@ 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 to // "https://github.com/lightbend/config/blob/v1.2.1/config/src/main/java/com/typesafe/config/Config.java#L165" diff --git a/project/plugins.sbt b/project/plugins.sbt index 75b78fbee..3ed275faa 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,10 +1,9 @@ -addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.4.0") -addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.6") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") +addSbtPlugin("com.github.sbt" % "sbt-findbugs" % "2.0.0") +addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "3.1.0") +addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1") addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.3") -addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1") +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") From 8863d6c03548a95a57e6ed871d7d80d77c488a97 Mon Sep 17 00:00:00 2001 From: Enno Runne <458526+ennru@users.noreply.github.com> Date: Sun, 1 Jul 2018 16:54:37 +0200 Subject: [PATCH 22/83] Scalariform settings in root build.sbt --- build.sbt | 11 +++++++++-- config/build.sbt | 10 ---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/build.sbt b/build.sbt index 026af3434..e9040beff 100644 --- a/build.sbt +++ b/build.sbt @@ -2,6 +2,7 @@ // 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" @@ -22,7 +23,10 @@ val sonatype = new PublishToSonatype { } lazy val commonSettings: Seq[Setting[_]] = Def.settings( - unpublished + unpublished, + scalariformPreferences := scalariformPreferences.value + .setPreference(IndentSpaces, 4) + .setPreference(FirstArgumentOnNewline, Preserve) ) lazy val root = (project in file(".")) @@ -49,7 +53,10 @@ lazy val configLib = Project("config", file("config")) publish := sys.error("use publishSigned instead of plain publish"), publishLocal := sys.error("use publishLocalSigned instead of plain publishLocal"), packageOptions in (Compile, packageBin) += - Package.ManifestAttributes("Automatic-Module-Name" -> "typesafe.config" ) + Package.ManifestAttributes("Automatic-Module-Name" -> "typesafe.config" ), + scalariformPreferences := scalariformPreferences.value + .setPreference(IndentSpaces, 4) + .setPreference(FirstArgumentOnNewline, Preserve) ) .enablePlugins(SbtOsgi) .dependsOn(testLib % "test->test") diff --git a/config/build.sbt b/config/build.sbt index 8b5c49e76..59e80a43a 100644 --- a/config/build.sbt +++ b/config/build.sbt @@ -1,13 +1,3 @@ -import com.typesafe.sbt.SbtScalariform -import com.typesafe.sbt.SbtScalariform.ScalariformKeys -import scalariform.formatter.preferences._ - -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 From 59e3597cd2fa4d58f6c4f32ab3ae5862832dea0b Mon Sep 17 00:00:00 2001 From: Enno Runne <458526+ennru@users.noreply.github.com> Date: Sun, 1 Jul 2018 16:55:31 +0200 Subject: [PATCH 23/83] Reformatted with Scalariform --- .../typesafe/config/impl/ConfigValueTest.scala | 16 ++++++++-------- .../com/typesafe/config/impl/TestUtils.scala | 2 +- .../com/typesafe/config/impl/TokenizerTest.scala | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) 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 9d1a49a45..db593f1de 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala @@ -280,13 +280,13 @@ class ConfigValueTest extends TestUtils { } /** - * Reproduces the issue #461. - *

- * 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 #461. + *

+ * 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 } 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..702c2930c 100644 --- a/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala +++ b/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala @@ -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) 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..ac7a8ac39 100644 --- a/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala @@ -198,7 +198,7 @@ class TokenizerTest extends TestUtils { """\"\""", // file ends with a backslash "$", // file ends with a $ "${" // file ends with a ${ - ) + ) for (t <- invalidTests) { val tokenized = tokenizeAsList(t) From 2330d47e05f3c247f73690c1319a981406be110b Mon Sep 17 00:00:00 2001 From: Ash Date: Fri, 13 Jul 2018 11:31:50 +0100 Subject: [PATCH 24/83] Minor correction to the use of config.root().render() under "Debugging Your Configuration" Looking at the java/scaladoc of ConfigValue.render() (https://github.com/lightbend/config/blob/master/config/src/main/java/com/typesafe/config/ConfigValue.java#L48-L50) and then looking at the implementation of `render()` itself: https://github.com/lightbend/config/blob/master/config/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java#L364-L374 it appears that `config.root().render()` returns a string that must be printed out, logged etc. Current documentation implies that calling this method will print out the string which seems to be misleading. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 18b617b36..715ed3b44 100644 --- a/README.md +++ b/README.md @@ -758,8 +758,10 @@ 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. ### Supports Java 8 and Later From c326c2245414f5d8116786100efc8a8a452d717f Mon Sep 17 00:00:00 2001 From: Radist Date: Sat, 14 Jul 2018 16:41:52 +0300 Subject: [PATCH 25/83] @Optional in getters for properties without field --- .../java/com/typesafe/config/impl/ConfigBeanImpl.java | 2 +- config/src/test/java/beanconfig/ObjectsConfig.java | 10 ++++++++++ .../typesafe/config/impl/ConfigBeanFactoryTest.scala | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) 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 66d059ba1..2b9cf5c60 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java @@ -285,7 +285,7 @@ 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; } private static Field getField(Class beanClass, String fieldName) { 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/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala index 00e5a093c..bd4b47043 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigBeanFactoryTest.scala @@ -211,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) } From 2851e491f11f03278c7b5868052773a99288e819 Mon Sep 17 00:00:00 2001 From: The Viet Nguyen Date: Wed, 25 Jul 2018 18:50:05 +0300 Subject: [PATCH 26/83] Add syntax from file name parser option --- .../typesafe/config/ConfigParseOptions.java | 14 +++++++++ .../typesafe/config/impl/ConfigImplUtil.java | 20 +++++++++++++ .../com/typesafe/config/impl/Parseable.java | 17 ++--------- config/src/test/resources/test01.properties | 1 + .../config/impl/ParseableReaderTest.scala | 30 +++++++++++++++++++ .../com/typesafe/config/impl/UtilTest.scala | 26 ++++++++++++++++ 6 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala 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/impl/ConfigImplUtil.java b/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java index 279f5ec84..9a13ea84c 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,23 @@ static String toCamelCase(String originalName) { } return nameBuilder.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; + 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/Parseable.java b/config/src/main/java/com/typesafe/config/impl/Parseable.java index ae54a7d42..ad5146301 100644 --- a/config/src/main/java/com/typesafe/config/impl/Parseable.java +++ b/config/src/main/java/com/typesafe/config/impl/Parseable.java @@ -326,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"); } @@ -574,7 +563,7 @@ protected Reader reader(ConfigParseOptions options) throws IOException { @Override ConfigSyntax guessSyntax() { - return syntaxFromExtension(input.getPath()); + return ConfigImplUtil.syntaxFromExtension(input.getPath()); } @Override @@ -643,7 +632,7 @@ protected Reader reader() throws IOException { @Override ConfigSyntax guessSyntax() { - return syntaxFromExtension(input.getName()); + return ConfigImplUtil.syntaxFromExtension(input.getName()); } @Override @@ -756,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/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/scala/com/typesafe/config/impl/ParseableReaderTest.scala b/config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala new file mode 100644 index 000000000..70182df54 --- /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/UtilTest.scala b/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala index cf31a7166..bccbaa476 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._ @@ -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)) + } } From 744073fd50c842e14fa2b9c50cd8bd226ace397c Mon Sep 17 00:00:00 2001 From: The Viet Nguyen Date: Wed, 25 Jul 2018 19:11:50 +0300 Subject: [PATCH 27/83] Unify guess syntax decision tree --- .../src/main/java/com/typesafe/config/impl/ConfigImplUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9a13ea84c..1dcc43d3d 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java @@ -244,7 +244,7 @@ static String toCamelCase(String originalName) { public static ConfigSyntax syntaxFromExtension(String filename) { if (filename == null) return null; - if (filename.endsWith(".json")) + else if (filename.endsWith(".json")) return ConfigSyntax.JSON; else if (filename.endsWith(".conf")) return ConfigSyntax.CONF; From 9b9d3f91e8238a55cade9259ef01d3ddd8e9a6fd Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 31 Jul 2018 15:04:41 +0900 Subject: [PATCH 28/83] #575 Remove wording about apps having reference.conf --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 715ed3b44..e35c756b4 100644 --- a/README.md +++ b/README.md @@ -233,9 +233,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 From 04b791b6e78a1c3452e95399884d171e38b417e1 Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Wed, 8 Aug 2018 00:02:02 -0700 Subject: [PATCH 29/83] sbt 1.2.1 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index d6e35076c..5620cc502 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.1.6 +sbt.version=1.2.1 From 00c84869bf5eab5a2585123d080181f530109814 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 2 Jul 2015 20:24:09 -0400 Subject: [PATCH 30/83] Reimplement ResolveMemos with a ridiculous hand-rolled hash table This is intended to band-aid #330 with a hash table that sucks, but ought to be much faster to copy than the stock Java one. The stock Java hash table rehashes everything every time, while this hash table can often just copy an array. That said, I didn't benchmark it or anything, it may well also be super slow. This is more or less a troll commit to get someone to do better. --- .../java/com/typesafe/config/impl/BadMap.java | 140 ++++++++++++++++++ .../typesafe/config/impl/ResolveMemos.java | 12 +- 2 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 config/src/main/java/com/typesafe/config/impl/BadMap.java 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..820fb3044 --- /dev/null +++ b/config/src/main/java/com/typesafe/config/impl/BadMap.java @@ -0,0 +1,140 @@ +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 { + 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; + } + } + + final int size; + final Entry[] entries; + + private final static Entry[] emptyEntries = {}; + + BadMap() { + this(0, (Entry[]) emptyEntries); + } + + private BadMap(int size, Entry[] entries) { + this.size = size; + this.entries = entries; + } + + BadMap copyingPut(K k, V v) { + int newSize = size + 1; + Entry[] newEntries; + // The "- 1" is so we pick size 2 when newSize is 1. + int threshold = (newSize * 2) - 1; + if (threshold > (entries.length * 2)) { + // nextPrime doesn't always return a prime larger than + // we passed in, so this block may not actually change + // the entries size. + newEntries = new Entry[nextPrime(threshold)]; + } 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 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 old : src) { + if (old != null) { + if (old.next == null) { + store(dest, old); + } else { + store(dest, old.hash, old.key, old.value); + store(dest, old.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 + }; + + private final 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/ResolveMemos.java b/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java index cf98a900d..4fe96b3ea 100644 --- a/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java +++ b/config/src/main/java/com/typesafe/config/impl/ResolveMemos.java @@ -11,14 +11,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 memos; + final private BadMap memos; - private ResolveMemos(Map memos) { + private ResolveMemos(BadMap memos) { this.memos = memos; } ResolveMemos() { - this(new HashMap()); + this(new BadMap()); } AbstractConfigValue get(MemoKey key) { @@ -26,10 +26,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 copy = new HashMap(memos); - copy.put(key, value); - return new ResolveMemos(copy); + return new ResolveMemos(memos.copyingPut(key, value)); } } From 9215e057a825807a6e01a569f1b44c58b5c755a9 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Fri, 3 Jul 2015 09:38:47 -0400 Subject: [PATCH 31/83] minor cleanups to BadMap --- .../main/java/com/typesafe/config/impl/BadMap.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/impl/BadMap.java b/config/src/main/java/com/typesafe/config/impl/BadMap.java index 820fb3044..0c59eba9d 100644 --- a/config/src/main/java/com/typesafe/config/impl/BadMap.java +++ b/config/src/main/java/com/typesafe/config/impl/BadMap.java @@ -46,13 +46,12 @@ private BadMap(int size, Entry[] entries) { BadMap copyingPut(K k, V v) { int newSize = size + 1; Entry[] newEntries; - // The "- 1" is so we pick size 2 when newSize is 1. - int threshold = (newSize * 2) - 1; - if (threshold > (entries.length * 2)) { + 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. - newEntries = new Entry[nextPrime(threshold)]; + // 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]; } @@ -126,7 +125,7 @@ V get(K k) { 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 + 2053, 3079, 4057, 7103, 10949, 16069, 32609, 65867, 104729 }; private final static int nextPrime(int i) { From c08ffd25f3922343a287dbe5fd76ed9035b4d5f6 Mon Sep 17 00:00:00 2001 From: Sam Holeman Date: Fri, 10 Aug 2018 15:07:23 -0700 Subject: [PATCH 32/83] Tweak BadMap, add tests --- .../java/com/typesafe/config/impl/BadMap.java | 34 ++++--- .../typesafe/config/impl/ResolveMemos.java | 5 +- .../com/typesafe/config/impl/BadMapTest.scala | 94 +++++++++++++++++++ 3 files changed, 111 insertions(+), 22 deletions(-) create mode 100644 config/src/test/scala/com/typesafe/config/impl/BadMapTest.scala diff --git a/config/src/main/java/com/typesafe/config/impl/BadMap.java b/config/src/main/java/com/typesafe/config/impl/BadMap.java index 0c59eba9d..04b375367 100644 --- a/config/src/main/java/com/typesafe/config/impl/BadMap.java +++ b/config/src/main/java/com/typesafe/config/impl/BadMap.java @@ -29,13 +29,13 @@ else if (next != null) } } - final int size; - final Entry[] entries; + private final int size; + private final Entry[] entries; private final static Entry[] emptyEntries = {}; BadMap() { - this(0, (Entry[]) emptyEntries); + this(0, emptyEntries); } private BadMap(int size, Entry[] entries) { @@ -51,19 +51,20 @@ BadMap copyingPut(K k, V v) { // 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)]; + newEntries = new Entry[nextPrime((newSize * 2) - 1)]; } else { newEntries = new Entry[entries.length]; } - if (newEntries.length == entries.length) + if (newEntries.length == entries.length) { System.arraycopy(entries, 0, newEntries, 0, entries.length); - else + } else { rehash(entries, newEntries); + } int hash = Math.abs(k.hashCode()); store(newEntries, hash, k, v); - return new BadMap(newSize, newEntries); + return new BadMap<>(newSize, newEntries); } private static void store(Entry[] entries, int hash, K k, V v) { @@ -72,7 +73,7 @@ private static void store(Entry[] entries, int hash, K k, V v) { entries[i] = new Entry(hash, k, v, old); } - private static void store(Entry[] entries, Entry e) { + 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) { @@ -84,15 +85,12 @@ private static void store(Entry[] entries, Entry e) { } } - private static void rehash(Entry[] src, Entry[] dest) { - for (Entry old : src) { - if (old != null) { - if (old.next == null) { - store(dest, old); - } else { - store(dest, old.hash, old.key, old.value); - store(dest, old.next); - } + 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; } } } @@ -128,7 +126,7 @@ V get(K k) { 2053, 3079, 4057, 7103, 10949, 16069, 32609, 65867, 104729 }; - private final static int nextPrime(int i) { + private static int nextPrime(int i) { for (int p : primes) { if (p > i) return p; 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 4fe96b3ea..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 @@ -18,7 +15,7 @@ private ResolveMemos(BadMap memos) { } ResolveMemos() { - this(new BadMap()); + this(new BadMap<>()); } AbstractConfigValue get(MemoKey key) { 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 + } +} From f46ab201879687d51970fa34f7855b34a0823be0 Mon Sep 17 00:00:00 2001 From: "k.bigwheel" Date: Wed, 15 Aug 2018 04:10:13 +0900 Subject: [PATCH 33/83] Remove nbsp that breaking Markdown rendering --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e35c756b4..88c1559ba 100644 --- a/README.md +++ b/README.md @@ -837,7 +837,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 From 56c7d75aec75088616d8dbe054506450bbc56cc0 Mon Sep 17 00:00:00 2001 From: vgolub Date: Fri, 7 Sep 2018 18:13:23 +0300 Subject: [PATCH 34/83] #584 Add root config origin to exception --- .../src/main/java/com/typesafe/config/ConfigException.java | 5 +++++ config/src/main/java/com/typesafe/config/impl/Path.java | 4 ++-- .../src/main/java/com/typesafe/config/impl/SimpleConfig.java | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigException.java b/config/src/main/java/com/typesafe/config/ConfigException.java index fce04b407..f781b3755 100644 --- a/config/src/main/java/com/typesafe/config/ConfigException.java +++ b/config/src/main/java/com/typesafe/config/ConfigException.java @@ -8,6 +8,7 @@ import java.lang.reflect.Field; import com.typesafe.config.impl.ConfigImplUtil; +import com.typesafe.config.impl.Path; /** * All exceptions thrown by the library are subclasses of @@ -126,6 +127,10 @@ public Missing(String path, Throwable cause) { cause); } + public Missing(ConfigOrigin origin, Path path) { + this(origin, "No configuration setting found for key '" + path.render() + "'"); + } + public Missing(String path) { this(path, null); } diff --git a/config/src/main/java/com/typesafe/config/impl/Path.java b/config/src/main/java/com/typesafe/config/impl/Path.java index d39a57c68..1e0fd7471 100644 --- a/config/src/main/java/com/typesafe/config/impl/Path.java +++ b/config/src/main/java/com/typesafe/config/impl/Path.java @@ -7,7 +7,7 @@ import com.typesafe.config.ConfigException; -final class Path { +public final class Path { final private String first; final private Path remainder; @@ -216,7 +216,7 @@ public String toString() { * toString() is a debugging-oriented version while this is an * error-message-oriented human-readable one. */ - String render() { + public String render() { StringBuilder sb = new StringBuilder(); appendToStringBuilder(sb); return sb.toString(); 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 ce1913adb..308d14ca7 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -153,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); if (expected != null) v = DefaultTransformer.transform(v, expected); From c2c6303a32a0d63daec3cf75c4548dfe02147206 Mon Sep 17 00:00:00 2001 From: vgolub Date: Fri, 7 Sep 2018 20:25:46 +0300 Subject: [PATCH 35/83] Do not pass Path object as exception argument --- .../main/java/com/typesafe/config/ConfigException.java | 8 ++------ config/src/main/java/com/typesafe/config/impl/Path.java | 4 ++-- .../main/java/com/typesafe/config/impl/SimpleConfig.java | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigException.java b/config/src/main/java/com/typesafe/config/ConfigException.java index f781b3755..f35e8dbea 100644 --- a/config/src/main/java/com/typesafe/config/ConfigException.java +++ b/config/src/main/java/com/typesafe/config/ConfigException.java @@ -8,7 +8,6 @@ import java.lang.reflect.Field; import com.typesafe.config.impl.ConfigImplUtil; -import com.typesafe.config.impl.Path; /** * All exceptions thrown by the library are subclasses of @@ -127,8 +126,8 @@ public Missing(String path, Throwable cause) { cause); } - public Missing(ConfigOrigin origin, Path path) { - this(origin, "No configuration setting found for key '" + path.render() + "'"); + public Missing(ConfigOrigin origin, String path) { + this(origin, "No configuration setting found for key '" + path + "'", null); } public Missing(String path) { @@ -139,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); - } } /** diff --git a/config/src/main/java/com/typesafe/config/impl/Path.java b/config/src/main/java/com/typesafe/config/impl/Path.java index 1e0fd7471..d39a57c68 100644 --- a/config/src/main/java/com/typesafe/config/impl/Path.java +++ b/config/src/main/java/com/typesafe/config/impl/Path.java @@ -7,7 +7,7 @@ import com.typesafe.config.ConfigException; -public final class Path { +final class Path { final private String first; final private Path remainder; @@ -216,7 +216,7 @@ public String toString() { * toString() is a debugging-oriented version while this is an * error-message-oriented human-readable one. */ - public String render() { + String render() { StringBuilder sb = new StringBuilder(); appendToStringBuilder(sb); return sb.toString(); 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 308d14ca7..c0d6b65c7 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -153,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(self.origin(), originalPath); + throw new ConfigException.Missing(self.origin(), originalPath.render()); if (expected != null) v = DefaultTransformer.transform(v, expected); From 87cf1f1ba309aae03d7e4aed3d0db7e5f328341c Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 17 Sep 2018 00:27:44 -0400 Subject: [PATCH 36/83] Remove bad resolver Ref https://github.com/sbt/sbt/issues/4363 http://scala-tools.org/repo-snapshots/ is no longer a valid resolver. Since it now redirects to https://blog.goodstuff.im/repo-snapshots, it causes "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target" --- config/build.sbt | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/build.sbt b/config/build.sbt index 59e80a43a..a588d747e 100644 --- a/config/build.sbt +++ b/config/build.sbt @@ -12,8 +12,6 @@ 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 := { From 3a1ffb1af77751ca23e5e315d0c38f159619517d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 17 Sep 2018 00:30:55 -0400 Subject: [PATCH 37/83] Refactor build to unified slash syntax --- build.sbt | 189 ++++++++++++++++++++++---------- config/build.sbt | 68 ------------ project/PublishToSonatype.scala | 48 -------- project/plugins.sbt | 1 + 4 files changed, 132 insertions(+), 174 deletions(-) delete mode 100644 config/build.sbt delete mode 100644 project/PublishToSonatype.scala diff --git a/build.sbt b/build.sbt index e9040beff..07ab4b181 100644 --- a/build.sbt +++ b/build.sbt @@ -4,62 +4,145 @@ // Release tags should follow: http://semver.org/ import scalariform.formatter.preferences._ -enablePlugins(GitVersioning) -git.baseVersion := "1.3.0" - -organization in GlobalScope := "com.typesafe" - -scalacOptions in GlobalScope in Compile := Seq("-unchecked", "-deprecation", "-feature") -scalacOptions in GlobalScope in Test := Seq("-unchecked", "-deprecation", "-feature") - -scalaVersion in ThisBuild := "2.10.4" - -val sonatype = new PublishToSonatype { - def projectUrl = "https://github.com/lightbend/config" - def developerId = "havocp" - def developerName = "Havoc Pennington" - def developerUrl = "http://ometer.com/" - def scmUrl = "git://github.com/lightbend/config.git" -} - -lazy val commonSettings: Seq[Setting[_]] = Def.settings( - unpublished, - scalariformPreferences := scalariformPreferences.value - .setPreference(IndentSpaces, 4) - .setPreference(FirstArgumentOnNewline, Preserve) +ThisBuild / git.baseVersion := "1.3.0" +ThisBuild / organization := "com.typesafe" +ThisBuild / Compile / scalacOptions := List("-unchecked", "-deprecation", "-feature") +ThisBuild / Test / scalacOptions := List("-unchecked", "-deprecation", "-feature") +ThisBuild / scalaVersion := "2.10.6" + +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 lazy val root = (project in file(".")) - .settings( - commonSettings, - 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 - ) + .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 + }) lazy val configLib = Project("config", file("config")) - .settings( - sonatype.settings, - osgiSettings, - 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"), - packageOptions in (Compile, packageBin) += - Package.ManifestAttributes("Automatic-Module-Name" -> "typesafe.config" ), - scalariformPreferences := scalariformPreferences.value - .setPreference(IndentSpaces, 4) - .setPreference(FirstArgumentOnNewline, Preserve) - ) .enablePlugins(SbtOsgi) .dependsOn(testLib % "test->test") + .settings(osgiSettings) + .settings(nocomma { + autoScalaLibrary := false + crossPaths := false + libraryDependencies += "net.liftweb" %% "lift-json" % "2.5" % Test + libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test + + Compile / compile / javacOptions ++= Seq("-source", "1.8", "-target", "1.8", + "-g", "-Xlint:unchecked") + + 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 + + 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") + + 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) +) def proj(id: String, base: File) = Project(id, base) settings commonSettings @@ -73,23 +156,13 @@ lazy val simpleLibJava = proj("config-simple-lib-java", file("examples/java/si 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 -useGpg := true - -aggregate in PgpKeys.publishSigned := false -PgpKeys.publishSigned := (PgpKeys.publishSigned in configLib).value - -aggregate in PgpKeys.publishLocalSigned := false -PgpKeys.publishLocalSigned := (PgpKeys.publishLocalSigned in configLib).value - val unpublished = Seq( // no artifacts in this project - publishArtifact := false, + publishArtifact := false, // make-pom has a more specific publishArtifact setting already // so needs specific override - publishArtifact in makePom := false, + makePom / publishArtifact := 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 := {} + packageDoc / publishArtifact := false, + publish / skip := true ) diff --git a/config/build.sbt b/config/build.sbt deleted file mode 100644 index a588d747e..000000000 --- a/config/build.sbt +++ /dev/null @@ -1,68 +0,0 @@ -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" - -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)).value - -findbugsReportType := Some(FindbugsReport.Html) -findbugsReportPath := Some(crossTarget.value / "findbugs.html") -findbugsEffort := FindbugsEffort.Maximum -findbugsMaxMemory := 2000 - -javacOptions in (Compile, compile) ++= Seq("-source", "1.8", "-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/lightbend/config/blob/$gitHead/config/src/main/java" -} diff --git a/project/PublishToSonatype.scala b/project/PublishToSonatype.scala deleted file mode 100644 index 9c12d5cd9..000000000 --- a/project/PublishToSonatype.scala +++ /dev/null @@ -1,48 +0,0 @@ -import sbt._, Keys._ - -// 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 = "https://www.apache.org/licenses/LICENSE-2.0" - def licenseDistribution = "repo" - def scmUrl: String - def scmConnection = "scm:git:" + scmUrl - - def generatePomExtra: xml.NodeSeq = { - { projectUrl } - - - { licenseName } - { licenseUrl } - { licenseDistribution } - - - - { scmUrl } - { scmConnection } - - - - { developerId } - { developerName } - { developerUrl } - - - } - - def settings: Seq[Setting[_]] = Seq( - publishMavenStyle := true, - publishTo := Some(if (isSnapshot.value) ossSnapshots else ossStaging), - publishArtifact in Test := false, - pomIncludeRepository := (_ => false), - pomExtra := generatePomExtra - ) -} diff --git a/project/plugins.sbt b/project/plugins.sbt index 3ed275faa..dd15b5dd1 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -7,3 +7,4 @@ addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2") addSbtPlugin("com.etsy" % "sbt-checkstyle-plugin" % "3.1.1") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") +addSbtPlugin("com.eed3si9n" % "sbt-nocomma" % "0.1.0") From 362b7ecccd85d1bef45e41862b83f216712a3f65 Mon Sep 17 00:00:00 2001 From: Eric Richardson Date: Thu, 1 Nov 2018 11:24:34 -0400 Subject: [PATCH 38/83] Fix stack trace printing in test --- config/src/test/scala/com/typesafe/config/impl/TestUtils.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 702c2930c..8b6787d1d 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) } From 1c23940ddd5b2296b576def5720e5fb8550ab053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sun, 4 Nov 2018 16:46:48 +0100 Subject: [PATCH 39/83] Add a note about classloader issues to README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 88c1559ba..cf034c778 100644 --- a/README.md +++ b/README.md @@ -759,6 +759,14 @@ If you have trouble with your configuration, some useful tips. 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` to manually - e.g. with `ConfigFactory.load(getClass().getClassLoader())`. + 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 From a7625e5387fe39081f6ac0083854c9bfede34b4d Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sun, 4 Nov 2018 17:00:15 +0100 Subject: [PATCH 40/83] Update README.md Co-Authored-By: kubukoz --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf034c778..6c340120c 100644 --- a/README.md +++ b/README.md @@ -763,7 +763,8 @@ If you have trouble with your configuration, some useful tips. `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` to manually - e.g. with `ConfigFactory.load(getClass().getClassLoader())`. + 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. From f3628e4c6b5e7ce8162fb5d5dce0def938fa89ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sun, 4 Nov 2018 17:02:02 +0100 Subject: [PATCH 41/83] Remove whitespace --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c340120c..ea89fd62f 100644 --- a/README.md +++ b/README.md @@ -764,7 +764,7 @@ If you have trouble with your configuration, some useful tips. 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. + 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. From f82a1ed44c26a927a52186a9616bf79b085aeb36 Mon Sep 17 00:00:00 2001 From: hepin1989 Date: Mon, 3 Dec 2018 15:19:09 +0800 Subject: [PATCH 42/83] Update sbt to 1.2.7. --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 5620cc502..72f902892 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.1 +sbt.version=1.2.7 From 855716ae7e84cfc31c6ff68266dffbc8edb1b6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Mela?= Date: Sat, 5 Jan 2019 09:02:59 +0100 Subject: [PATCH 43/83] Project Scala version updated to 1.12.8; all the IDE & compiler warnings fixed As discussed in https://github.com/lightbend/config/pull/568 . `lift-json` was also bumped as there was no longer any suitable version for Scala 2.12.x. Also fixed compiler warnings (e.g. _eta expansion of parameterless methods is deprecated_) as well as some rarther non-controversial IDE warnings (e.g. redundant semicolons and such). --- build.sbt | 4 +- config/src/test/scala/ApiExamples.scala | 2 +- config/src/test/scala/Profiling.scala | 26 +++++------ config/src/test/scala/Rendering.scala | 2 +- .../typesafe/config/impl/ConfParserTest.scala | 18 ++++---- .../config/impl/ConfigBeanFactoryTest.scala | 14 +++--- .../impl/ConfigDocumentParserTest.scala | 23 +++++----- .../config/impl/ConfigDocumentTest.scala | 4 +- .../config/impl/ConfigSubstitutionTest.scala | 14 +++--- .../com/typesafe/config/impl/ConfigTest.scala | 45 +++++++++---------- .../config/impl/ConfigValueTest.scala | 42 ++++++++--------- .../config/impl/EquivalentsTest.scala | 2 +- .../com/typesafe/config/impl/HttpTest.scala | 2 +- .../com/typesafe/config/impl/JsonTest.scala | 27 +++++------ .../config/impl/ParseableReaderTest.scala | 36 +++++++-------- .../com/typesafe/config/impl/PathTest.scala | 4 +- .../typesafe/config/impl/PropertiesTest.scala | 2 +- .../com/typesafe/config/impl/TestUtils.scala | 2 +- .../typesafe/config/impl/TokenizerTest.scala | 12 ++--- .../com/typesafe/config/impl/ToyHttp.scala | 9 ++-- .../typesafe/config/impl/UnitParserTest.scala | 4 +- .../com/typesafe/config/impl/UtilTest.scala | 2 +- .../src/main/scala/ComplexApp.scala | 2 +- .../src/main/scala/simplelib/SimpleLib.scala | 6 +-- project/linksource.scala | 4 +- 25 files changed, 150 insertions(+), 158 deletions(-) diff --git a/build.sbt b/build.sbt index 07ab4b181..9d78b6aea 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ ThisBuild / git.baseVersion := "1.3.0" ThisBuild / organization := "com.typesafe" ThisBuild / Compile / scalacOptions := List("-unchecked", "-deprecation", "-feature") ThisBuild / Test / scalacOptions := List("-unchecked", "-deprecation", "-feature") -ThisBuild / scalaVersion := "2.10.6" +ThisBuild / scalaVersion := "2.12.8" ThisBuild / scmInfo := Option( ScmInfo(url("https://github.com/lightbend/config"), "scm:git@github.com:lightbend/config.git") @@ -63,7 +63,7 @@ lazy val configLib = Project("config", file("config")) .settings(nocomma { autoScalaLibrary := false crossPaths := false - libraryDependencies += "net.liftweb" %% "lift-json" % "2.5" % Test + libraryDependencies += "net.liftweb" %% "lift-json" % "3.3.0" % Test libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test Compile / compile / javacOptions ++= Seq("-source", "1.8", "-target", "1.8", 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..efdb0904f 100644 --- a/config/src/test/scala/Rendering.scala +++ b/config/src/test/scala/Rendering.scala @@ -62,7 +62,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/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 bd4b47043..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,10 +122,10 @@ 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") @@ -155,10 +155,10 @@ 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") @@ -200,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")) } 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 0becca4bc..1bf031ba3 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -25,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 { @@ -33,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 @@ -46,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: " + @@ -352,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 @@ -402,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")) } @@ -568,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 @@ -951,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")) @@ -983,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 @@ -1107,11 +1104,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) { @@ -1134,7 +1131,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) @@ -1269,7 +1266,7 @@ class ConfigTest extends TestUtils { } private def runFallbackTest(expected: String, source: String, - allowUnresolved: Boolean, resolvers: ConfigResolver*) = { + allowUnresolved: Boolean, resolvers: ConfigResolver*): Unit = { val unresolved = ConfigFactory.parseString(source) var options = ConfigResolveOptions.defaults().setAllowUnresolved(allowUnresolved) for (resolver <- resolvers) 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 db593f1de..ca17e8a98 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) } @@ -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) { 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 index 70182df54..3ded66625 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ParseableReaderTest.scala @@ -2,29 +2,29 @@ package com.typesafe.config.impl import java.io.InputStreamReader -import com.typesafe.config.{ConfigException, ConfigFactory, ConfigParseOptions} +import com.typesafe.config.{ ConfigException, ConfigFactory, ConfigParseOptions } import org.hamcrest.CoreMatchers.containsString -import org.junit.Assert.{assertEquals, assertThat} +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 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) + @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 '^'")) } - 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/TestUtils.scala b/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala index 8b6787d1d..d33e215fe 100644 --- a/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala +++ b/config/src/test/scala/com/typesafe/config/impl/TestUtils.scala @@ -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 ac7a8ac39..79750bc83 100644 --- a/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/TokenizerTest.scala @@ -202,8 +202,8 @@ class TokenizerTest extends TestUtils { 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 a06a40c19..0a7f7e3e3 100644 --- a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala @@ -44,7 +44,7 @@ class UnitParserTest extends TestUtils { } @Test - def parsePeriod() = { + def parsePeriod(): Unit = { val oneYears = List( "1y", "1 y", "1year", "1 years", " 1y ", " 1 y ", "365", "365d", "365 d", "365 days", " 365 days ", "365day", @@ -157,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 bccbaa476..6d0bb160c 100644 --- a/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/UtilTest.scala @@ -58,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) 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/linksource.scala b/project/linksource.scala index 70f49fc0d..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 From 86d9ea0d5b1a909095838de8e3aa33f287fbe3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Mela?= Date: Tue, 10 Jul 2018 20:58:41 +0200 Subject: [PATCH 44/83] Fixed support for keys that are all digits but longer than an int. Previously such config keys would still be parsed with `Integer#parseInt` for the sake of sorting, resulting in a `NumberFormatException`. Now the keys consisting of digits only are parsed to a `BigInteger` and compared as such. This addresses the following issues: https://github.com/lightbend/config/issues/604 https://github.com/lightbend/config/issues/541 --- .../config/impl/SimpleConfigObject.java | 8 ++-- config/src/test/resources/test12.conf | 43 +++++++++++++++++++ config/src/test/scala/Rendering.scala | 1 + .../config/impl/ConfigValueTest.scala | 4 +- 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 config/src/test/resources/test12.conf 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/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/scala/Rendering.scala b/config/src/test/scala/Rendering.scala index 0d8be469e..e1ca3cf6d 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 { 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 db593f1de..de97288c3 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala @@ -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) } } From d468cd5ea43d3c37510ddf2a3a5a5e6eb3b8380c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Thu, 24 Jan 2019 09:54:41 +0100 Subject: [PATCH 45/83] Use AdoptOpenJDK --- .travis.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c62ad817..df74bfb98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,24 @@ # use Docker-based container (instead of OpenVZ) sudo: false -cache: - directories: - - $HOME/.ivy2/cache - - $HOME/.sbt/boot - language: scala -jdk: - - oraclejdk8 +before_install: + # using jabba for custom jdk management + - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.11.2/install.sh | bash && . ~/.jabba/jabba.sh + - jabba install adopt@1.8.202-08 + - java -version script: - sbt ++$TRAVIS_SCALA_VERSION test doc +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 From 7e268070cd8f44f95f3a7739423acb845d115690 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 28 Jan 2019 15:44:58 +0000 Subject: [PATCH 46/83] Drop note on Akka 2.0/Play 2.0 I initially misread the note to mean Akka/Play 2.x. Seeing as Akka 2.0 and Play 2.0 is close to 7 years ago, we can lose the note now? * https://github.com/akka/akka/releases/tag/v2.0 * https://github.com/playframework/playframework/releases/tag/2.0 --- HOCON.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/HOCON.md b/HOCON.md index 6e0818c05..9f8bd837d 100644 --- a/HOCON.md +++ b/HOCON.md @@ -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 From 7f8e587f72a700a4eb2593ae8ad30973c69fa997 Mon Sep 17 00:00:00 2001 From: Eric Richardson Date: Thu, 14 Feb 2019 12:15:09 -0500 Subject: [PATCH 47/83] Add new Scala port to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ea89fd62f..ff594afeb 100644 --- a/README.md +++ b/README.md @@ -867,6 +867,7 @@ format. #### Scala port * SHocon https://github.com/unicredit/shocon (work with both Scala and Scala.Js) + * sconfig https://github.com/ekrich/sconfig #### Ruby port From d56d5906669c6cd4885ecbdd2d1c9daef8d62b61 Mon Sep 17 00:00:00 2001 From: Eric Richardson Date: Thu, 14 Feb 2019 15:48:15 -0500 Subject: [PATCH 48/83] Update Shocon entry --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff594afeb..28d51af89 100644 --- a/README.md +++ b/README.md @@ -866,7 +866,7 @@ format. #### 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 #### Ruby port From 1622208ebd0ed2ea48261c49ba194f9bb2db5b7f Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Wed, 27 Feb 2019 12:37:37 +0000 Subject: [PATCH 49/83] Add a config strategy to load from environment variables first. --- config/checkstyle-config.xml | 2 +- config/checkstyle-suppressions.xml | 2 +- .../config/EnvFirstConfigLoadingStrategy.java | 79 +++++++++++++++++++ .../com/typesafe/config/impl/ConfigTest.scala | 47 +++++++++++ .../typesafe/config/impl/PublicApiTest.scala | 29 +++++++ 5 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java diff --git a/config/checkstyle-config.xml b/config/checkstyle-config.xml index b5b71d2b6..0fc2c197a 100644 --- a/config/checkstyle-config.xml +++ b/config/checkstyle-config.xml @@ -1,7 +1,7 @@ + "https://checkstyle.org/dtds/configuration_1_3.dtd"> diff --git a/config/checkstyle-suppressions.xml b/config/checkstyle-suppressions.xml index 21fb8e51e..6fa3fdc00 100644 --- a/config/checkstyle-suppressions.xml +++ b/config/checkstyle-suppressions.xml @@ -1,6 +1,6 @@ + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + * Environment variables are mangled in the following way after stripping the prefix: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Env VarConfig
_   [1 underscore]. [dot]
__  [2 underscore]- [dash]
___ [3 underscore]_ [underscore]
+ * + *

+ * A variable like: {@code CONFIG_a_b__c___d} + * is translated to a config key: {@code a.b-c_d} + * + *

+ * The prefix may be altered by defining the VM property {@code config.env_var_prefix} + */ +public class EnvFirstConfigLoadingStrategy extends DefaultConfigLoadingStrategy { + + protected static Map env = new HashMap(System.getenv()); + + @Override + public Config parseApplicationConfig(ConfigParseOptions parseOptions) { + String envVarPrefix = System.getProperty("config.env_var_prefix"); + if (envVarPrefix == null) // fallback to default + envVarPrefix = "CONFIG_"; + + Map defaultsFromEnv = new HashMap(); + for (String key : env.keySet()) { + if (key.startsWith(envVarPrefix)) { + StringBuilder builder = new StringBuilder(); + + String strippedPrefix = key.substring(envVarPrefix.length(), key.length()); + + int underscores = 0; + for (char c : strippedPrefix.toCharArray()) { + if (c == '_') { + underscores++; + } else { + switch (underscores) { + case 1: builder.append('.'); + break; + case 2: builder.append('-'); + break; + case 3: builder.append('_'); + break; + } + underscores = 0; + builder.append(c); + } + } + + String propertyKey = builder.toString(); + defaultsFromEnv.put(propertyKey, env.get(key)); + } + } + + return ConfigFactory.parseMap(defaultsFromEnv).withFallback(super.parseApplicationConfig(parseOptions)); + } +} 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 1bf031ba3..f5084c79d 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -1090,6 +1090,53 @@ class ConfigTest extends TestUtils { assertEquals(10, resolved.getInt("bar.nested.a.q")) } + @Test + def testLoadWithEnvSubstitutions() { + TestEnvFirstStrategy.putEnvVar("CONFIG_42___a", "1") + TestEnvFirstStrategy.putEnvVar("CONFIG_a_b_c", "2") + TestEnvFirstStrategy.putEnvVar("CONFIG_a__c", "3") + TestEnvFirstStrategy.putEnvVar("CONFIG_a___c", "4") + + TestEnvFirstStrategy.putEnvVar("CONFIG_akka_version", "foo") + TestEnvFirstStrategy.putEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size", "10") + + System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName) + + 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.strategy") + + TestEnvFirstStrategy.removeEnvVar("CONFIG_42___a") + TestEnvFirstStrategy.removeEnvVar("CONFIG_a_b_c") + TestEnvFirstStrategy.removeEnvVar("CONFIG_a__c") + TestEnvFirstStrategy.removeEnvVar("CONFIG_a___c") + + TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_version") + TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size") + } + } + @Test def renderRoundTrip() { val allBooleans = true :: false :: Nil 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 d25d43659..136ea690a 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,28 @@ class PublicApiTest extends TestUtils { } } + @Test + def supportsEnvFirstConfigLoadingStrategy(): Unit = { + assertEquals("config.strategy is not set", null, System.getProperty("config.strategy")) + + TestEnvFirstStrategy.putEnvVar("CONFIG_a", "5") + System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName) + + try { + val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(), + Map("reference.conf" -> resourceFile("a_1.conf").toURI.toURL())) + + val configA1 = withContextClassLoader(loaderA1) { + ConfigFactory.load() + } + + assertEquals(5, configA1.getInt("a")) + } finally { + System.clearProperty("config.strategy") + TestEnvFirstStrategy.removeEnvVar("CONFIG_a") + } + } + @Test def usesContextClassLoaderForApplicationConf() { val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(), @@ -1145,4 +1167,11 @@ object TestStrategy { private var invocations = 0 def getIncovations() = invocations def increment() = invocations += 1 +} + +object TestEnvFirstStrategy extends EnvFirstConfigLoadingStrategy { + def putEnvVar(key: String, value: String) = + EnvFirstConfigLoadingStrategy.env.put(key, value) + def removeEnvVar(key: String) = + EnvFirstConfigLoadingStrategy.env.remove(key) } \ No newline at end of file From 7d7380fe040375bead3e3989649e030530f50d6c Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Wed, 27 Feb 2019 12:39:46 +0000 Subject: [PATCH 50/83] Fix suppressions doctype --- config/checkstyle-suppressions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/checkstyle-suppressions.xml b/config/checkstyle-suppressions.xml index 6fa3fdc00..62d514067 100644 --- a/config/checkstyle-suppressions.xml +++ b/config/checkstyle-suppressions.xml @@ -1,6 +1,6 @@ + "https://checkstyle.org/dtds/suppressions_1_1.dtd"> Date: Wed, 27 Feb 2019 12:41:15 +0000 Subject: [PATCH 51/83] Trailing blank line --- .../src/test/scala/com/typesafe/config/impl/PublicApiTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 136ea690a..5eed422ac 100644 --- a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala @@ -1174,4 +1174,4 @@ object TestEnvFirstStrategy extends EnvFirstConfigLoadingStrategy { EnvFirstConfigLoadingStrategy.env.put(key, value) def removeEnvVar(key: String) = EnvFirstConfigLoadingStrategy.env.remove(key) -} \ No newline at end of file +} From 8a14342ca5678b79d164726df92c06a5a7468b0c Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Mon, 4 Mar 2019 14:43:12 +0000 Subject: [PATCH 52/83] Env variables resolution moved to defaultOverrides --- build.sbt | 12 ++- .../com/typesafe/config/ConfigFactory.java | 59 +++++++++++++- .../config/EnvFirstConfigLoadingStrategy.java | 79 ------------------- .../com/typesafe/config/impl/ConfigImpl.java | 52 ++++++++++++ .../com/typesafe/config/impl/ConfigTest.scala | 20 +---- .../typesafe/config/impl/PublicApiTest.scala | 25 ++---- 6 files changed, 130 insertions(+), 117 deletions(-) delete mode 100644 config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java diff --git a/build.sbt b/build.sbt index 9d78b6aea..15260557a 100644 --- a/build.sbt +++ b/build.sbt @@ -85,7 +85,17 @@ lazy val configLib = Project("config", file("config")) Test/ run / fork := true //env vars for tests - Test / envVars ++= Map("testList.0" -> "0", "testList.1" -> "1") + 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") diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java index 86d995e36..8405520f5 100644 --- a/config/src/main/java/com/typesafe/config/ConfigFactory.java +++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java @@ -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() { } @@ -383,7 +384,11 @@ public static Config defaultReference(ClassLoader loader) { * @return the default override configuration */ public static Config defaultOverrides() { - return systemProperties(); + if (!getOverrideWithEnv()) { + return systemProperties(); + } else { + return systemEnvironmentOverrides().withFallback(systemProperties()); + } } /** @@ -394,7 +399,7 @@ public static Config defaultOverrides() { * @return the default override configuration */ public static Config defaultOverrides(ClassLoader loader) { - return systemProperties(); + return defaultOverrides(); } /** @@ -549,6 +554,50 @@ public static Config systemProperties() { return ConfigImpl.systemPropertiesAsConfig(); } + /** + * Gets a Config containing the system's environment variables + * used to override configuration keys. + * Environment variables taken in considerations are starting with + * {@code CONFIG_FORCE_} + * + *

+ * Environment variables are mangled in the following way after stripping the prefix "CONFIG_FORCE_": + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Env VarConfig
_   [1 underscore]. [dot]
__  [2 underscore]- [dash]
___ [3 underscore]_ [underscore]
+ * + *

+ * A variable like: {@code CONFIG_FORCE_a_b__c___d} + * is translated to a config key: {@code a.b-c_d} + * + *

+ * This method can return a global immutable singleton, so it's preferred + * over parsing system properties yourself. + *

+ * {@link #defaultOverrides} will include the system system environment variables as + * overrides if `config.override_with_env_vars` is set to `true`. + * + * @return system environment variable overrides parsed into a Config + */ + public static Config systemEnvironmentOverrides() { + return ConfigImpl.envVariablesOverridesAsConfig(); + } + /** * Gets a Config containing the system's environment variables. * This method can return a global immutable singleton. @@ -1063,4 +1112,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/EnvFirstConfigLoadingStrategy.java b/config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java deleted file mode 100644 index 0b0948035..000000000 --- a/config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.typesafe.config; - -import java.util.Map; -import java.util.HashMap; - -/** - * Environment variables first config loading strategy. Able to load environment variables first and fallback to {@link DefaultConfigLoadingStrategy} - * - *

- * Environment variables are mangled in the following way after stripping the prefix: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Env VarConfig
_   [1 underscore]. [dot]
__  [2 underscore]- [dash]
___ [3 underscore]_ [underscore]
- * - *

- * A variable like: {@code CONFIG_a_b__c___d} - * is translated to a config key: {@code a.b-c_d} - * - *

- * The prefix may be altered by defining the VM property {@code config.env_var_prefix} - */ -public class EnvFirstConfigLoadingStrategy extends DefaultConfigLoadingStrategy { - - protected static Map env = new HashMap(System.getenv()); - - @Override - public Config parseApplicationConfig(ConfigParseOptions parseOptions) { - String envVarPrefix = System.getProperty("config.env_var_prefix"); - if (envVarPrefix == null) // fallback to default - envVarPrefix = "CONFIG_"; - - Map defaultsFromEnv = new HashMap(); - for (String key : env.keySet()) { - if (key.startsWith(envVarPrefix)) { - StringBuilder builder = new StringBuilder(); - - String strippedPrefix = key.substring(envVarPrefix.length(), key.length()); - - int underscores = 0; - for (char c : strippedPrefix.toCharArray()) { - if (c == '_') { - underscores++; - } else { - switch (underscores) { - case 1: builder.append('.'); - break; - case 2: builder.append('-'); - break; - case 3: builder.append('_'); - break; - } - underscores = 0; - builder.append(c); - } - } - - String propertyKey = builder.toString(); - defaultsFromEnv.put(propertyKey, env.get(key)); - } - } - - return ConfigFactory.parseMap(defaultsFromEnv).withFallback(super.parseApplicationConfig(parseOptions)); - } -} 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..f9057ede5 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; @@ -360,6 +361,57 @@ public static void reloadEnvVariablesConfig() { EnvVariablesHolder.envVariables = loadEnvVariables(); } + private static AbstractConfigObject loadEnvVariablesOverrides() { + Map env = new HashMap(System.getenv()); + Map result = new HashMap(System.getenv()); + for (String key : env.keySet()) { + if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) { + StringBuilder builder = new StringBuilder(); + + String strippedPrefix = key.substring(ENV_VAR_OVERRIDE_PREFIX.length(), key.length()); + + int underscores = 0; + for (char c : strippedPrefix.toCharArray()) { + if (c == '_') { + underscores++; + } else { + switch (underscores) { + case 1: builder.append('.'); + break; + case 2: builder.append('-'); + break; + case 3: builder.append('_'); + break; + } + underscores = 0; + builder.append(c); + } + } + + String propertyKey = builder.toString(); + result.put(propertyKey, 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 Config defaultReference(final ClassLoader loader) { return computeCachedConfig(loader, "defaultReference", new Callable() { @Override 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 f5084c79d..853aa249f 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -1092,15 +1092,7 @@ class ConfigTest extends TestUtils { @Test def testLoadWithEnvSubstitutions() { - TestEnvFirstStrategy.putEnvVar("CONFIG_42___a", "1") - TestEnvFirstStrategy.putEnvVar("CONFIG_a_b_c", "2") - TestEnvFirstStrategy.putEnvVar("CONFIG_a__c", "3") - TestEnvFirstStrategy.putEnvVar("CONFIG_a___c", "4") - - TestEnvFirstStrategy.putEnvVar("CONFIG_akka_version", "foo") - TestEnvFirstStrategy.putEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size", "10") - - System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName) + System.setProperty("config.override_with_env_vars", "true") try { val loader02 = new TestClassLoader(this.getClass().getClassLoader(), @@ -1125,15 +1117,7 @@ class ConfigTest extends TestUtils { assertEquals("foo", conf04.getString("akka.version")) assertEquals(10, conf04.getInt("akka.event-handler-dispatcher.max-pool-size")) } finally { - System.clearProperty("config.strategy") - - TestEnvFirstStrategy.removeEnvVar("CONFIG_42___a") - TestEnvFirstStrategy.removeEnvVar("CONFIG_a_b_c") - TestEnvFirstStrategy.removeEnvVar("CONFIG_a__c") - TestEnvFirstStrategy.removeEnvVar("CONFIG_a___c") - - TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_version") - TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size") + System.clearProperty("config.override_with_env_vars") } } 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 5eed422ac..4c72643ea 100644 --- a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala @@ -654,24 +654,22 @@ class PublicApiTest extends TestUtils { } @Test - def supportsEnvFirstConfigLoadingStrategy(): Unit = { - assertEquals("config.strategy is not set", null, System.getProperty("config.strategy")) + def loadEnvironmentVariablesOverridesIfConfigured(): Unit = { + assertEquals("config.override_with_env_vars is not set", null, System.getProperty("config.override_with_env_vars")) - TestEnvFirstStrategy.putEnvVar("CONFIG_a", "5") - System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName) + System.setProperty("config.override_with_env_vars", "true") try { - val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(), - Map("reference.conf" -> resourceFile("a_1.conf").toURI.toURL())) + val loaderB2 = new TestClassLoader(this.getClass().getClassLoader(), + Map("reference.conf" -> resourceFile("b_2.conf").toURI.toURL())) - val configA1 = withContextClassLoader(loaderA1) { + val configB2 = withContextClassLoader(loaderB2) { ConfigFactory.load() } - assertEquals(5, configA1.getInt("a")) + assertEquals(5, configB2.getInt("b")) } finally { - System.clearProperty("config.strategy") - TestEnvFirstStrategy.removeEnvVar("CONFIG_a") + System.clearProperty("config.override_with_env_vars") } } @@ -1168,10 +1166,3 @@ object TestStrategy { def getIncovations() = invocations def increment() = invocations += 1 } - -object TestEnvFirstStrategy extends EnvFirstConfigLoadingStrategy { - def putEnvVar(key: String, value: String) = - EnvFirstConfigLoadingStrategy.env.put(key, value) - def removeEnvVar(key: String) = - EnvFirstConfigLoadingStrategy.env.remove(key) -} From 81ae9d93ff2b5b6de836da3954450459427150bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 8 Mar 2019 08:54:37 +0100 Subject: [PATCH 53/83] add new Rust port to README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 28d51af89..916d757b0 100644 --- a/README.md +++ b/README.md @@ -893,6 +893,10 @@ format. * https://github.com/akkadotnet/HOCON +#### Rust port + + * https://github.com/mockersf/hocon.rs + #### Linting tool * A web based linting tool http://www.hoconlint.com/ From bf57edb7f220682caf186421ad2c7bb9934504d5 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Fri, 8 Mar 2019 14:26:47 +0000 Subject: [PATCH 54/83] fixed review comments --- .../src/main/java/com/typesafe/config/ConfigFactory.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java index 8405520f5..4937c8d3b 100644 --- a/config/src/main/java/com/typesafe/config/ConfigFactory.java +++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java @@ -384,10 +384,10 @@ public static Config defaultReference(ClassLoader loader) { * @return the default override configuration */ public static Config defaultOverrides() { - if (!getOverrideWithEnv()) { - return systemProperties(); - } else { + if (getOverrideWithEnv()) { return systemEnvironmentOverrides().withFallback(systemProperties()); + } else { + return systemProperties(); } } @@ -589,7 +589,7 @@ public static Config systemProperties() { * This method can return a global immutable singleton, so it's preferred * over parsing system properties yourself. *

- * {@link #defaultOverrides} will include the system system environment variables as + * {@link #defaultOverrides} will include the system environment variables as * overrides if `config.override_with_env_vars` is set to `true`. * * @return system environment variable overrides parsed into a Config From 5d8ba54679ef091cf21b206e8872dd067dea697d Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Fri, 8 Mar 2019 14:53:14 +0000 Subject: [PATCH 55/83] Added a description of the new property in Readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 28d51af89..d967fd6fc 100644 --- a/README.md +++ b/README.md @@ -675,6 +675,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 From d38b91904f7bf300ce0e983a923cc9332cb76b93 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Fri, 15 Mar 2019 16:40:59 +0000 Subject: [PATCH 56/83] Detail resons for _ sustitutions --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d967fd6fc..23ef139e4 100644 --- a/README.md +++ b/README.md @@ -693,6 +693,15 @@ With this option enabled only environment variables starting with i.e. The environment variable `CONFIG_FORCE_a_b__c___d` set the configuration key `a.b-c_d` +Rationale the 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. + ### Concatenation Values _on the same line_ are concatenated (for strings and From a05923dd7f08828eb0937b5b1c68559065660435 Mon Sep 17 00:00:00 2001 From: Aaron Pritzlaff Date: Tue, 19 Mar 2019 17:34:49 +0000 Subject: [PATCH 57/83] Added reference to args4c for command-line-to-config utils --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 916d757b0..681a4f62d 100644 --- a/README.md +++ b/README.md @@ -856,6 +856,7 @@ format. * 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 From f0816e556b903a3df5e50d0fb90a6f0c001dd132 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 21 Mar 2019 08:52:42 +0000 Subject: [PATCH 58/83] Move rationale to code and implement invalidate cache --- README.md | 9 --------- .../java/com/typesafe/config/ConfigFactory.java | 1 + .../java/com/typesafe/config/impl/ConfigImpl.java | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 23ef139e4..d967fd6fc 100644 --- a/README.md +++ b/README.md @@ -693,15 +693,6 @@ With this option enabled only environment variables starting with i.e. The environment variable `CONFIG_FORCE_a_b__c___d` set the configuration key `a.b-c_d` -Rationale the 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. - ### Concatenation Values _on the same line_ are concatenated (for strings and diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java index 4937c8d3b..4b91a1ea4 100644 --- a/config/src/main/java/com/typesafe/config/ConfigFactory.java +++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java @@ -502,6 +502,7 @@ public static void invalidateCaches() { // all caches ConfigImpl.reloadSystemPropertiesConfig(); ConfigImpl.reloadEnvVariablesConfig(); + ConfigImpl.reloadEnvVariablesOverridesConfig(); } /** 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 f9057ede5..ffd2ba891 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java @@ -364,6 +364,14 @@ public static void reloadEnvVariablesConfig() { private static AbstractConfigObject loadEnvVariablesOverrides() { Map env = new HashMap(System.getenv()); Map result = new HashMap(System.getenv()); + // 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. for (String key : env.keySet()) { if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) { StringBuilder builder = new StringBuilder(); @@ -412,6 +420,12 @@ 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() { @Override From f686abd341fe2902d8fc629ebea15fdb5808804a Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 21 Mar 2019 09:02:48 +0000 Subject: [PATCH 59/83] Bump sbt version --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 72f902892..c0bab0494 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.7 +sbt.version=1.2.8 From fd7d9cc89ea7c76a50c0edff65686906389e798d Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 21 Mar 2019 10:01:27 +0000 Subject: [PATCH 60/83] Remove Puppy Crawl references --- config/checkstyle-config.xml | 5 ++--- config/checkstyle-suppressions.xml | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/checkstyle-config.xml b/config/checkstyle-config.xml index 0fc2c197a..064ac59a5 100644 --- a/config/checkstyle-config.xml +++ b/config/checkstyle-config.xml @@ -1,8 +1,7 @@ - - diff --git a/config/checkstyle-suppressions.xml b/config/checkstyle-suppressions.xml index 62d514067..ca3fb2d15 100644 --- a/config/checkstyle-suppressions.xml +++ b/config/checkstyle-suppressions.xml @@ -1,5 +1,6 @@ - From 0e3b472aadd26a9c42bb73f21e2ddd09dcea98ce Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 21 Mar 2019 10:50:48 +0000 Subject: [PATCH 61/83] Move env mangling to ConfigImplUtil --- .../com/typesafe/config/impl/ConfigImpl.java | 36 ++------------- .../typesafe/config/impl/ConfigImplUtil.java | 46 +++++++++++++++++++ .../com/typesafe/config/impl/ConfigTest.scala | 16 +++++++ 3 files changed, 66 insertions(+), 32 deletions(-) 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 ffd2ba891..e96913287 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java @@ -361,43 +361,15 @@ public static void reloadEnvVariablesConfig() { EnvVariablesHolder.envVariables = loadEnvVariables(); } + + private static AbstractConfigObject loadEnvVariablesOverrides() { Map env = new HashMap(System.getenv()); Map result = new HashMap(System.getenv()); - // 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. + for (String key : env.keySet()) { if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) { - StringBuilder builder = new StringBuilder(); - - String strippedPrefix = key.substring(ENV_VAR_OVERRIDE_PREFIX.length(), key.length()); - - int underscores = 0; - for (char c : strippedPrefix.toCharArray()) { - if (c == '_') { - underscores++; - } else { - switch (underscores) { - case 1: builder.append('.'); - break; - case 2: builder.append('-'); - break; - case 3: builder.append('_'); - break; - } - underscores = 0; - builder.append(c); - } - } - - String propertyKey = builder.toString(); - result.put(propertyKey, env.get(key)); + result.put(ConfigImplUtil.envVariableAsProperty(key, ENV_VAR_OVERRIDE_PREFIX), env.get(key)); } } 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 1dcc43d3d..1a8ffc1db 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigImplUtil.java @@ -235,6 +235,52 @@ 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. * 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 853aa249f..eeda74e70 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -1090,6 +1090,22 @@ 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") From 2efd1fa06ad210f82f688d67a6ec1b3afec8e5a7 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 21 Mar 2019 10:53:10 +0000 Subject: [PATCH 62/83] minor fix --- config/checkstyle-config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/checkstyle-config.xml b/config/checkstyle-config.xml index 064ac59a5..ae2259fa7 100644 --- a/config/checkstyle-config.xml +++ b/config/checkstyle-config.xml @@ -1,6 +1,6 @@ From 4836b452ef3fa80d803df9230573550137ac3b5c Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 21 Mar 2019 10:54:31 +0000 Subject: [PATCH 63/83] fix again XML --- config/checkstyle-config.xml | 2 +- config/checkstyle-suppressions.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/checkstyle-config.xml b/config/checkstyle-config.xml index ae2259fa7..148167f31 100644 --- a/config/checkstyle-config.xml +++ b/config/checkstyle-config.xml @@ -1,5 +1,5 @@ - diff --git a/config/checkstyle-suppressions.xml b/config/checkstyle-suppressions.xml index ca3fb2d15..300751899 100644 --- a/config/checkstyle-suppressions.xml +++ b/config/checkstyle-suppressions.xml @@ -1,5 +1,5 @@ - From 93cb38a60d9c933aa43de656dcfdc2cfb84fb115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Thu, 18 Apr 2019 12:29:27 +0000 Subject: [PATCH 64/83] Add release notes for 1.3.4 (#628) * Add release notes for 1.3.4 --- NEWS.md | 12 ++++++++++++ README.md | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 12fd05e14..a4e5b84f4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,15 @@ +# 1.3.4: April 18, 2018 + +- 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. diff --git a/README.md b/README.md index 7389e5e99..5e6a3529d 100644 --- a/README.md +++ b/README.md @@ -106,12 +106,12 @@ You can find published releases on Maven Central. com.typesafe config - 1.3.2 + 1.3.4 sbt dependency: - libraryDependencies += "com.typesafe" % "config" % "1.3.2" + libraryDependencies += "com.typesafe" % "config" % "1.3.4" Link for direct download if you don't use a dependency manager: From 152366baf609c02c680e11b28d307d37709d260a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Thu, 18 Apr 2019 15:57:40 +0300 Subject: [PATCH 65/83] Update to the latest sbt-pgp --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index dd15b5dd1..38196f0ac 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ addSbtPlugin("com.github.sbt" % "sbt-findbugs" % "2.0.0") addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "3.1.0") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1") +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") From 0f36308a0b7bf88bcfee9dc33355ab91859d2137 Mon Sep 17 00:00:00 2001 From: Mario Cornejo Date: Fri, 26 Apr 2019 16:53:53 +0200 Subject: [PATCH 66/83] Update NEWS.md Typo in the year --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index a4e5b84f4..3dffc59e9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# 1.3.4: April 18, 2018 +# 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) From a9981371e2d191e6e6d8986e3ebe50ef652c6d6b Mon Sep 17 00:00:00 2001 From: Arnout Engelen Date: Tue, 7 May 2019 15:21:54 +0200 Subject: [PATCH 67/83] Test on JDK 11 --- .travis.yml | 9 ++++++++- build.sbt | 3 ++- .../main/java/com/typesafe/config/impl/ConfigImpl.java | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index df74bfb98..8e7e88b54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,11 +7,18 @@ before_install: # using jabba for custom jdk management - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.11.2/install.sh | bash && . ~/.jabba/jabba.sh - jabba install adopt@1.8.202-08 - - java -version + - jabba install adopt@1.11.0-1 script: + - jabba use $JDK + - java -version - sbt ++$TRAVIS_SCALA_VERSION test doc +jobs: + include: + - env: JDK=adopt@1.8.202-08 + - env: JDK=adopt@1.11.0-1 + before_cache: # Remove to avoid unnecessary cache updates - find $HOME/.sbt -name "*.lock" -delete diff --git a/build.sbt b/build.sbt index 15260557a..8a6b87f78 100644 --- a/build.sbt +++ b/build.sbt @@ -151,7 +151,8 @@ lazy val commonSettings: Seq[Setting[_]] = Def.settings( unpublished, scalariformPreferences := scalariformPreferences.value .setPreference(IndentSpaces, 4) - .setPreference(FirstArgumentOnNewline, Preserve) + .setPreference(FirstArgumentOnNewline, Preserve), + Compile / compile / javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), ) def proj(id: String, base: File) = Project(id, base) settings commonSettings 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 e96913287..c807046db 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java @@ -302,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 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; } From 0dd6c2237f74c8561834b701947394c53234e1b4 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 7 May 2019 16:19:25 +0200 Subject: [PATCH 68/83] newer jdk11 Co-Authored-By: raboof --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8e7e88b54..eb52df64c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_install: # using jabba for custom jdk management - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.11.2/install.sh | bash && . ~/.jabba/jabba.sh - jabba install adopt@1.8.202-08 - - jabba install adopt@1.11.0-1 + - jabba install adopt@1.11.0-3 script: - jabba use $JDK From 4f8d507ecae827d87c043f3abd45b0e5c9c50c8d Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 7 May 2019 16:19:36 +0200 Subject: [PATCH 69/83] Newer jdk8 Co-Authored-By: raboof --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eb52df64c..471c939bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ script: jobs: include: - - env: JDK=adopt@1.8.202-08 + - env: JDK=adopt@1.8.212-03 - env: JDK=adopt@1.11.0-1 before_cache: From 12abec262970489894a076b093a422267f644a53 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 7 May 2019 16:19:51 +0200 Subject: [PATCH 70/83] Use latest jabba Co-Authored-By: raboof --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 471c939bb..55cb53de3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ language: scala before_install: # using jabba for custom jdk management - - curl -sL https://raw.githubusercontent.com/shyiko/jabba/0.11.2/install.sh | bash && . ~/.jabba/jabba.sh + - curl -sL https://git.io/jabba | bash && . ~/.jabba/jabba.sh - jabba install adopt@1.8.202-08 - jabba install adopt@1.11.0-3 From 215ad7eae5007157bbf3975793b6f7a0fc3ec5ca Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 7 May 2019 16:20:09 +0200 Subject: [PATCH 71/83] Use latest jdk8 Co-Authored-By: raboof --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 55cb53de3..a2033bba9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ language: scala before_install: # using jabba for custom jdk management - curl -sL https://git.io/jabba | bash && . ~/.jabba/jabba.sh - - jabba install adopt@1.8.202-08 + - jabba install adopt@1.8.212-03 - jabba install adopt@1.11.0-3 script: From 691da05f22c50c03c10e409410b222979a806ac7 Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 7 May 2019 16:20:31 +0200 Subject: [PATCH 72/83] Use latest jdk11 Co-Authored-By: raboof --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a2033bba9..09fa68d4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ script: jobs: include: - env: JDK=adopt@1.8.212-03 - - env: JDK=adopt@1.11.0-1 + - env: JDK=adopt@1.11.0-3 before_cache: # Remove to avoid unnecessary cache updates From cec08b7168df5b5266b5f66f6dab59f3ec321d9a Mon Sep 17 00:00:00 2001 From: Marcos Pereira Date: Tue, 7 May 2019 10:24:03 -0400 Subject: [PATCH 73/83] Fix formatting detail --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09fa68d4b..66f6e229a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ language: scala before_install: # using jabba for custom jdk management - - curl -sL https://git.io/jabba | bash && . ~/.jabba/jabba.sh - - jabba install adopt@1.8.212-03 + - curl -sL https://git.io/jabba | bash && . ~/.jabba/jabba.sh + - jabba install adopt@1.8.212-03 - jabba install adopt@1.11.0-3 script: From 4c83d9a860bc22180a4c58f4bdfb71bc86fc643f Mon Sep 17 00:00:00 2001 From: Eric Richardson Date: Wed, 22 May 2019 11:13:09 -0700 Subject: [PATCH 74/83] Update README to include sconfig supported platforms --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e6a3529d..b05f03d5d 100644 --- a/README.md +++ b/README.md @@ -886,7 +886,7 @@ format. #### Scala port * SHocon https://github.com/akka-js/shocon (Supports Scala.js and Scala Native) - * sconfig https://github.com/ekrich/sconfig + * sconfig https://github.com/ekrich/sconfig (Supports JVM, Scala Native, and Scala.js) #### Ruby port From 364b9ad1ceceddeb9223fb8063cf2003f99f4658 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 23 May 2019 06:14:00 -0400 Subject: [PATCH 75/83] Fix spelling typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e6a3529d..cd2d2e84b 100644 --- a/README.md +++ b/README.md @@ -924,7 +924,7 @@ format. * https://hocon-playground.herokuapp.com/ -# Maintanance notes +# Maintenance notes ## License From 97f3032b3e9b46ebf4731b791be52381d08e12d8 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 23 May 2019 07:11:22 -0400 Subject: [PATCH 76/83] =?UTF-8?q?Note=20in=20README=20that=20substitutions?= =?UTF-8?q?=20don=E2=80=99t=20work=20inside=20quotes=20(#634)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Note in README that substitutions don’t work inside quotes There’s more on this in HOCON.md but can’t hurt to repeat, people do miss it. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e2f05b0d..79f8de1b6 100644 --- a/README.md +++ b/README.md @@ -706,9 +706,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: From 14c882dab8fafa41d0c1fd9927ef44795660c4ac Mon Sep 17 00:00:00 2001 From: James Roper Date: Tue, 20 Aug 2019 23:02:50 +1000 Subject: [PATCH 77/83] Allow application.conf to override variables in reference.conf (#619) * Allow application.conf to override variables in reference.conf Fixes #167 This only affects the output of `ConfigFactory.load`. It does not change `ConfigFactory.defaultReference`. This uses the unresolved `reference.conf` in the building of configuration in `ConfigFactory.load`, effectively allowing `application.conf` properties to override variable substitutions in `reference.conf`. However, it still requires `reference.conf` to be fully resolvable, if it isn't, an exception will be thrown. So two resolves are still done during load, it's just that the output of the resolve of `reference.conf` isn't used in building the final configuration. The documentation has been updated to reflect this behavior. The reasoning behind this change can be read about in #167, but essentially, it is not uncommon for configuration properties to depend on each other by default, a good example of this is directory hierarchies, where you might have a configuration option for a base directory, and then a configuration for the log directory that by default is under the base directory, and within that a configuration for individual log files which by default are under the log directory. Without allowing variable substitutions in `reference.conf` from `application.conf`, there is no point in defining a configuration option for the base directory since changing it won't have any impact, and each path defined that depends on it will have to be manually overridden. This limitation is contrary to convention over configuration best practices, and hence not desirable in a configuration library. * Renamed public method to defaultReferenceUnresolved Also added the methods to ConfigFactory, as requested in code review. --- README.md | 28 ++-------- .../com/typesafe/config/ConfigException.java | 12 +++++ .../com/typesafe/config/ConfigFactory.java | 53 ++++++++++++++++++- .../com/typesafe/config/impl/ConfigImpl.java | 34 ++++++++++-- ...13-application-override-substitutions.conf | 1 + .../test13-reference-bad-substitutions.conf | 1 + .../test13-reference-with-substitutions.conf | 2 + .../typesafe/config/impl/PublicApiTest.scala | 22 ++++++++ 8 files changed, 125 insertions(+), 28 deletions(-) create mode 100644 config/src/test/resources/test13-application-override-substitutions.conf create mode 100644 config/src/test/resources/test13-reference-bad-substitutions.conf create mode 100644 config/src/test/resources/test13-reference-with-substitutions.conf diff --git a/README.md b/README.md index 79f8de1b6..5f24a47c1 100644 --- a/README.md +++ b/README.md @@ -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) @@ -277,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 @@ -758,14 +748,6 @@ concatenation, but not object/array concatenation. `+=` does not work in Play/Akka 2.0 either. Post-2.0 versions support these features. -### `reference.conf` can't refer to `application.conf` - -Please see this -earlier section; all `reference.conf` have substitutions -resolved first, without `application.conf` in the stack, so the -reference stack has to be self-contained. - ## Miscellaneous Notes ### Debugging Your Configuration diff --git a/config/src/main/java/com/typesafe/config/ConfigException.java b/config/src/main/java/com/typesafe/config/ConfigException.java index f35e8dbea..2e6d99ab7 100644 --- a/config/src/main/java/com/typesafe/config/ConfigException.java +++ b/config/src/main/java/com/typesafe/config/ConfigException.java @@ -284,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)); + } } /** diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java index 4b91a1ea4..12f9aaec0 100644 --- a/config/src/main/java/com/typesafe/config/ConfigFactory.java +++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java @@ -211,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); } @@ -368,6 +369,56 @@ public static Config defaultReference(ClassLoader loader) { return ConfigImpl.defaultReference(loader); } + /** + * Obtains the default reference configuration, which is currently created + * by merging all resources "reference.conf" found on the classpath and + * overriding the result with system properties. + * + *

+ * While the returned reference configuration is guaranteed to be + * resolvable (that is, there will be no substitutions that cannot be + * resolved), it is returned in an unresolved state for the purpose of + * allowing substitutions to be overridden by a config layer that falls + * back to this one. + * + *

+ * Libraries and frameworks should ship with a "reference.conf" in their + * jar. + * + *

+ * The reference config must be looked up in the class loader that contains + * the libraries that you want to use with this config, so the + * "reference.conf" for each library can be found. Use + * {@link #defaultReference(ClassLoader)} if the context class loader is not + * suitable. + * + *

+ * The {@link #load()} methods merge this configuration for you + * automatically. + * + *

+ * Future versions may look for reference configuration in more places. It + * is not guaranteed that this method only looks at + * "reference.conf". + * + * @return the unresolved default reference config for the context class + * loader + */ + public static Config defaultReferenceUnresolved() { + return defaultReferenceUnresolved(checkedContextClassLoader("defaultReferenceUnresolved")); + } + + /** + * Like {@link #defaultReferenceUnresolved()} but allows you to specify a + * class loader to use rather than the current context class loader. + * + * @param loader class loader to look for resources in + * @return the unresolved default reference config for this class loader + */ + public static Config defaultReferenceUnresolved(ClassLoader loader) { + return ConfigImpl.defaultReferenceUnresolved(loader); + } + /** * Obtains the default override configuration, which currently consists of * system properties. The returned override configuration will already have 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 c807046db..f14c12af6 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java @@ -408,15 +408,41 @@ public static Config defaultReference(final ClassLoader loader) { return computeCachedConfig(loader, "defaultReference", new Callable() { @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() { + @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/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/com/typesafe/config/impl/PublicApiTest.scala b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala index 4c72643ea..1643a4a31 100644 --- a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala @@ -1152,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 { From 558438aa2b0c05466e149a92ebef3133ce957edd Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin Date: Tue, 3 Sep 2019 21:35:07 +0300 Subject: [PATCH 78/83] Performance fix: added capacity parameter in ArrayLists contructor when adding elements --- config/src/main/java/com/typesafe/config/impl/ConfigParser.java | 2 +- .../main/java/com/typesafe/config/impl/SimpleConfigOrigin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 cce541636..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 values = new ArrayList(); + List values = new ArrayList(n.children().size()); for (AbstractConfigNode node : n.children()) { AbstractConfigValue v = null; 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 stack) { Iterator i = stack.iterator(); return mergeTwo((SimpleConfigOrigin) i.next(), (SimpleConfigOrigin) i.next()); } else { - List remaining = new ArrayList(); + List remaining = new ArrayList(stack.size()); for (ConfigOrigin o : stack) { remaining.add((SimpleConfigOrigin) o); } From 6c8d0d42271c01ca47dba76a7c841926077a811a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Andr=C3=A9n?= Date: Mon, 14 Oct 2019 10:53:41 +0200 Subject: [PATCH 79/83] Release 1.4.0 (#655) --- .travis.yml | 25 +++++++++++++------------ CONTRIBUTING.md | 36 +----------------------------------- NEWS.md | 7 +++++++ README.md | 4 ++-- RELEASING.md | 37 +++++++++++++++++++++++++++++++++++++ build.sbt | 2 +- 6 files changed, 61 insertions(+), 50 deletions(-) create mode 100644 RELEASING.md diff --git a/.travis.yml b/.travis.yml index 66f6e229a..980ed49e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,21 +3,22 @@ sudo: false language: scala -before_install: - # using jabba for custom jdk management - - curl -sL https://git.io/jabba | bash && . ~/.jabba/jabba.sh - - jabba install adopt@1.8.212-03 - - jabba install adopt@1.11.0-3 - -script: - - jabba use $JDK - - java -version - - sbt ++$TRAVIS_SCALA_VERSION test doc +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 jobs: include: - - env: JDK=adopt@1.8.212-03 - - env: JDK=adopt@1.11.0-3 + - 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30ebc7fd8..255892ab2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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/NEWS.md b/NEWS.md index 3dffc59e9..aeb66d0d5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,10 @@ +# 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) diff --git a/README.md b/README.md index 5f24a47c1..e2f29a466 100644 --- a/README.md +++ b/README.md @@ -105,12 +105,12 @@ You can find published releases on Maven Central. com.typesafe config - 1.3.4 + 1.4.0 sbt dependency: - libraryDependencies += "com.typesafe" % "config" % "1.3.4" + libraryDependencies += "com.typesafe" % "config" % "1.4.0" Link for direct download if you don't use a dependency manager: 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 8a6b87f78..631d5c8d9 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ // Release tags should follow: http://semver.org/ import scalariform.formatter.preferences._ -ThisBuild / 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") From 1ba4ed8cbaa76af911e0b5d854cb93eff1743e55 Mon Sep 17 00:00:00 2001 From: Christian Wiech Date: Sun, 8 Dec 2019 11:07:30 +0100 Subject: [PATCH 80/83] Make Optional inheritable (references #659) --- config/src/main/java/com/typesafe/config/Optional.java | 2 ++ 1 file changed, 2 insertions(+) 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 { From 90103f02ebaf8bf55b0e725fc4732b72b6ab2cbc Mon Sep 17 00:00:00 2001 From: Christian Wiech Date: Sun, 8 Dec 2019 11:10:52 +0100 Subject: [PATCH 81/83] Evaluate getter for optional annotation if field exists (references #659) --- .../typesafe/config/impl/ConfigBeanImpl.java | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) 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 2b9cf5c60..c434b9a22 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java @@ -27,21 +27,22 @@ import com.typesafe.config.Optional; /** - * Internal implementation detail, not ABI stable, do not touch. - * For use only by the {@link com.typesafe.config} package. + * Internal implementation detail, not ABI stable, do not touch. For use only by + * the {@link com.typesafe.config} package. */ public class ConfigBeanImpl { /** - * This is public ONLY for use by the "config" package, DO NOT USE this ABI - * may change. - * @param type of the bean + * This is public ONLY for use by the "config" package, DO NOT USE this ABI may + * change. + * + * @param type of the bean * @param config config to use - * @param clazz class of the bean + * @param clazz class of the bean * @return the bean instance */ public static T createInternal(Config config, Class clazz) { - if (((SimpleConfig)config).root().resolveStatus() != ResolveStatus.RESOLVED) + if (((SimpleConfig) config).root().resolveStatus() != ResolveStatus.RESOLVED) throw new ConfigException.NotResolved( "need to Config#resolve() a config before using it to initialize a bean, see the API docs for Config#resolve()"); @@ -126,9 +127,11 @@ public static T createInternal(Config config, Class clazz) { } return bean; } catch (InstantiationException e) { - throw new ConfigException.BadBean(clazz.getName() + " needs a public no-args constructor to be used as a bean", e); + throw new ConfigException.BadBean( + clazz.getName() + " needs a public no-args constructor to be used as a bean", e); } catch (IllegalAccessException e) { - throw new ConfigException.BadBean(clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e); + throw new ConfigException.BadBean( + clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e); } catch (InvocationTargetException e) { throw new ConfigException.BadBean("Calling bean method on " + clazz.getName() + " caused an exception", e); } @@ -141,7 +144,7 @@ public static T createInternal(Config config, Class clazz) { // of a nicer error message giving the name of the bad // setting. So, instead, we only support a limited number of // types plus you can always use Object, ConfigValue, Config, - // ConfigObject, etc. as an escape hatch. + // ConfigObject, etc. as an escape hatch. private static Object getValue(Class beanClass, Type parameterType, Class parameterClass, Config config, String configPropName) { if (parameterClass == Boolean.class || parameterClass == boolean.class) { @@ -166,9 +169,11 @@ private static Object getValue(Class beanClass, Type parameterType, 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(); + Type[] typeArgs = ((ParameterizedType) parameterType).getActualTypeArguments(); if (typeArgs[0] != String.class || typeArgs[1] != Object.class) { - throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported Map<" + typeArgs[0] + "," + typeArgs[1] + ">, only Map is supported right now"); + throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + + beanClass.getName() + " has unsupported Map<" + typeArgs[0] + "," + typeArgs[1] + + ">, only Map is supported right now"); } return config.getObject(configPropName).unwrapped(); } else if (parameterClass == Config.class) { @@ -186,16 +191,19 @@ private static Object getValue(Class beanClass, Type parameterType, Class } else if (hasAtLeastOneBeanProperty(parameterClass)) { return createInternal(config.getConfig(configPropName), parameterClass); } else { - throw new ConfigException.BadBean("Bean property " + configPropName + " of class " + beanClass.getName() + " has unsupported type " + parameterType); + throw new ConfigException.BadBean("Bean property " + configPropName + " of class " + beanClass.getName() + + " has unsupported type " + parameterType); } } - private static Object getSetValue(Class beanClass, Type parameterType, Class parameterClass, Config config, String configPropName) { + 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]; + private static Object getListValue(Class beanClass, Type parameterType, Class parameterClass, Config config, + String configPropName) { + Type elementType = ((ParameterizedType) parameterType).getActualTypeArguments()[0]; if (elementType == Boolean.class) { return config.getBooleanList(configPropName); @@ -231,7 +239,8 @@ private static Object getListValue(Class beanClass, Type parameterType, Class } return beanList; } else { - throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType); + throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + + " has unsupported list element type " + elementType); } } @@ -285,7 +294,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 : beanProp.getReadMethod().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) { From f3b6eca379d216b8bb0b75844f4693144afb0986 Mon Sep 17 00:00:00 2001 From: Christian Wiech Date: Sun, 8 Dec 2019 11:25:08 +0100 Subject: [PATCH 82/83] Revert "Evaluate getter for optional annotation if field exists (references #659) beacuse of accidently used code formatter" This reverts commit eafb1d84cbd48a1c378904a088c3992b6c2992ce. --- .../typesafe/config/impl/ConfigBeanImpl.java | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) 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 c434b9a22..2b9cf5c60 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java @@ -27,22 +27,21 @@ import com.typesafe.config.Optional; /** - * Internal implementation detail, not ABI stable, do not touch. For use only by - * the {@link com.typesafe.config} package. + * Internal implementation detail, not ABI stable, do not touch. + * For use only by the {@link com.typesafe.config} package. */ public class ConfigBeanImpl { /** - * This is public ONLY for use by the "config" package, DO NOT USE this ABI may - * change. - * - * @param type of the bean + * This is public ONLY for use by the "config" package, DO NOT USE this ABI + * may change. + * @param type of the bean * @param config config to use - * @param clazz class of the bean + * @param clazz class of the bean * @return the bean instance */ public static T createInternal(Config config, Class clazz) { - if (((SimpleConfig) config).root().resolveStatus() != ResolveStatus.RESOLVED) + if (((SimpleConfig)config).root().resolveStatus() != ResolveStatus.RESOLVED) throw new ConfigException.NotResolved( "need to Config#resolve() a config before using it to initialize a bean, see the API docs for Config#resolve()"); @@ -127,11 +126,9 @@ public static T createInternal(Config config, Class clazz) { } return bean; } catch (InstantiationException e) { - throw new ConfigException.BadBean( - clazz.getName() + " needs a public no-args constructor to be used as a bean", e); + throw new ConfigException.BadBean(clazz.getName() + " needs a public no-args constructor to be used as a bean", e); } catch (IllegalAccessException e) { - throw new ConfigException.BadBean( - clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e); + throw new ConfigException.BadBean(clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e); } catch (InvocationTargetException e) { throw new ConfigException.BadBean("Calling bean method on " + clazz.getName() + " caused an exception", e); } @@ -144,7 +141,7 @@ public static T createInternal(Config config, Class clazz) { // of a nicer error message giving the name of the bad // setting. So, instead, we only support a limited number of // types plus you can always use Object, ConfigValue, Config, - // ConfigObject, etc. as an escape hatch. + // ConfigObject, etc. as an escape hatch. private static Object getValue(Class beanClass, Type parameterType, Class parameterClass, Config config, String configPropName) { if (parameterClass == Boolean.class || parameterClass == boolean.class) { @@ -169,11 +166,9 @@ private static Object getValue(Class beanClass, Type parameterType, 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(); + Type[] typeArgs = ((ParameterizedType)parameterType).getActualTypeArguments(); if (typeArgs[0] != String.class || typeArgs[1] != Object.class) { - throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " - + beanClass.getName() + " has unsupported Map<" + typeArgs[0] + "," + typeArgs[1] - + ">, only Map is supported right now"); + throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported Map<" + typeArgs[0] + "," + typeArgs[1] + ">, only Map is supported right now"); } return config.getObject(configPropName).unwrapped(); } else if (parameterClass == Config.class) { @@ -191,19 +186,16 @@ private static Object getValue(Class beanClass, Type parameterType, Class } else if (hasAtLeastOneBeanProperty(parameterClass)) { return createInternal(config.getConfig(configPropName), parameterClass); } else { - throw new ConfigException.BadBean("Bean property " + configPropName + " of class " + beanClass.getName() - + " has unsupported type " + parameterType); + throw new ConfigException.BadBean("Bean property " + configPropName + " of class " + beanClass.getName() + " has unsupported type " + parameterType); } } - private static Object getSetValue(Class beanClass, Type parameterType, Class parameterClass, Config config, - String configPropName) { + 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]; + private static Object getListValue(Class beanClass, Type parameterType, Class parameterClass, Config config, String configPropName) { + Type elementType = ((ParameterizedType)parameterType).getActualTypeArguments()[0]; if (elementType == Boolean.class) { return config.getBooleanList(configPropName); @@ -239,8 +231,7 @@ private static Object getListValue(Class beanClass, Type parameterType, Class } return beanList; } else { - throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() - + " has unsupported list element type " + elementType); + throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType); } } @@ -294,10 +285,7 @@ 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 - || beanProp.getReadMethod().getAnnotationsByType(Optional.class).length > 0 - : beanProp.getReadMethod().getAnnotationsByType(Optional.class).length > 0; + return field != null ? field.getAnnotationsByType(Optional.class).length > 0 : beanProp.getReadMethod().getAnnotationsByType(Optional.class).length > 0; } private static Field getField(Class beanClass, String fieldName) { From 050cdb6bf7f51df9a5334312dd8cb32a2e96f1b0 Mon Sep 17 00:00:00 2001 From: Christian Wiech Date: Sun, 8 Dec 2019 11:27:57 +0100 Subject: [PATCH 83/83] Evaluate getter for optional annotation if field exists (references #659) --- .../main/java/com/typesafe/config/impl/ConfigBeanImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 2b9cf5c60..902311359 100644 --- a/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java +++ b/config/src/main/java/com/typesafe/config/impl/ConfigBeanImpl.java @@ -285,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 : beanProp.getReadMethod().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) {