mirror of
https://github.com/lightbend/config.git
synced 2025-01-28 21:20:07 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
3d44d90d4d
23
HOCON.md
23
HOCON.md
@ -30,7 +30,7 @@
|
|||||||
- [Include syntax](#include-syntax)
|
- [Include syntax](#include-syntax)
|
||||||
- [Include semantics: merging](#include-semantics-merging)
|
- [Include semantics: merging](#include-semantics-merging)
|
||||||
- [Include semantics: substitution](#include-semantics-substitution)
|
- [Include semantics: substitution](#include-semantics-substitution)
|
||||||
- [Include semantics: missing files](#include-semantics-missing-files)
|
- [Include semantics: missing files and required files](#include-semantics-missing-files-and-required-files)
|
||||||
- [Include semantics: file formats and extensions](#include-semantics-file-formats-and-extensions)
|
- [Include semantics: file formats and extensions](#include-semantics-file-formats-and-extensions)
|
||||||
- [Include semantics: locating resources](#include-semantics-locating-resources)
|
- [Include semantics: locating resources](#include-semantics-locating-resources)
|
||||||
- [Conversion of numerically-indexed objects to arrays](#conversion-of-numerically-indexed-objects-to-arrays)
|
- [Conversion of numerically-indexed objects to arrays](#conversion-of-numerically-indexed-objects-to-arrays)
|
||||||
@ -47,6 +47,7 @@
|
|||||||
- [Substitution fallback to environment variables](#substitution-fallback-to-environment-variables)
|
- [Substitution fallback to environment variables](#substitution-fallback-to-environment-variables)
|
||||||
- [hyphen-separated vs. camelCase](#hyphen-separated-vs-camelcase)
|
- [hyphen-separated vs. camelCase](#hyphen-separated-vs-camelcase)
|
||||||
- [Note on Java properties similarity](#note-on-java-properties-similarity)
|
- [Note on Java properties similarity](#note-on-java-properties-similarity)
|
||||||
|
- [Note on Windows and case sensitivity of environment variables](#note-on-windows-and-case-sensitivity-of-environment-variables)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
@ -1296,6 +1297,26 @@ must be lowercase. Exactly these strings are supported:
|
|||||||
- `h`, `hour`, `hours`
|
- `h`, `hour`, `hours`
|
||||||
- `d`, `day`, `days`
|
- `d`, `day`, `days`
|
||||||
|
|
||||||
|
### Period Format
|
||||||
|
|
||||||
|
Similar to the `getDuration()` method, there is a `getPeriod()` method
|
||||||
|
available for getting time units as a `java.time.Period`.
|
||||||
|
|
||||||
|
This can use the general "units format" described above; bare
|
||||||
|
numbers are taken to be in days, while strings are
|
||||||
|
parsed as a number plus an optional unit string.
|
||||||
|
|
||||||
|
The supported unit strings for period are case sensitive and
|
||||||
|
must be lowercase. Exactly these strings are supported:
|
||||||
|
|
||||||
|
- `d`, `day`, `days`
|
||||||
|
- `w`, `week`, `weeks`
|
||||||
|
- `m`, `mo`, `month`, `months` (note that if you are using `getTemporal()`
|
||||||
|
which may return either a `java.time.Duration` or a `java.time.Period`
|
||||||
|
you will want to use `mo` rather than `m` to prevent your unit being
|
||||||
|
parsed as minutes)
|
||||||
|
- `y`, `year`, `years`
|
||||||
|
|
||||||
### Size in bytes format
|
### Size in bytes format
|
||||||
|
|
||||||
Implementations may wish to support a `getBytes()` returning a
|
Implementations may wish to support a `getBytes()` returning a
|
||||||
|
14
NEWS.md
14
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
|
# 1.3.1: September 24, 2016
|
||||||
|
|
||||||
- added `include required("foo")` syntax to specify includes that
|
- added `include required("foo")` syntax to specify includes that
|
||||||
|
23
README.md
23
README.md
@ -81,11 +81,13 @@ to merge it in.
|
|||||||
- [Java (yep!) wrappers for the Java library](#java-yep-wrappers-for-the-java-library)
|
- [Java (yep!) wrappers for the Java library](#java-yep-wrappers-for-the-java-library)
|
||||||
- [Scala wrappers for the Java library](#scala-wrappers-for-the-java-library)
|
- [Scala wrappers for the Java library](#scala-wrappers-for-the-java-library)
|
||||||
- [Clojure wrappers for the Java library](#clojure-wrappers-for-the-java-library)
|
- [Clojure wrappers for the Java library](#clojure-wrappers-for-the-java-library)
|
||||||
|
- [Kotlin wrappers for the Java library](#kotlin-wrappers-for-the-java-library)
|
||||||
- [Scala port](#scala-port)
|
- [Scala port](#scala-port)
|
||||||
- [Ruby port](#ruby-port)
|
- [Ruby port](#ruby-port)
|
||||||
- [Puppet module](#puppet-module)
|
- [Puppet module](#puppet-module)
|
||||||
- [Python port](#python-port)
|
- [Python port](#python-port)
|
||||||
- [C++ port](#c-port)
|
- [C++ port](#c-port)
|
||||||
|
- [JavaScript port](#javascript-port)
|
||||||
- [Linting tool](#linting-tool)
|
- [Linting tool](#linting-tool)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
@ -196,7 +198,7 @@ There isn't a schema language or anything like that. However, two
|
|||||||
suggested tools are:
|
suggested tools are:
|
||||||
|
|
||||||
- use the
|
- use the
|
||||||
[checkValid() method](http://typesafehub.github.com/config/latest/api/com/typesafe/config/Config.html#checkValid%28com.typesafe.config.Config,%20java.lang.String...%29)
|
[checkValid() method](http://typesafehub.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
|
- access your config through a Settings class with a field for
|
||||||
each setting, and instantiate it on startup (immediately
|
each setting, and instantiate it on startup (immediately
|
||||||
throwing an exception if any settings are missing)
|
throwing an exception if any settings are missing)
|
||||||
@ -251,7 +253,8 @@ library examples in `examples/` show how to accept a custom config
|
|||||||
while defaulting to `ConfigFactory.load()`.
|
while defaulting to `ConfigFactory.load()`.
|
||||||
|
|
||||||
For applications using `application.{conf,json,properties}`,
|
For applications using `application.{conf,json,properties}`,
|
||||||
system properties can be used to force a different config source:
|
system properties can be used to force a different config source
|
||||||
|
(e.g. from command line `-Dconfig.file=path/to/config-file`):
|
||||||
|
|
||||||
- `config.resource` specifies a resource name - not a
|
- `config.resource` specifies a resource name - not a
|
||||||
basename, i.e. `application.conf` not `application`
|
basename, i.e. `application.conf` not `application`
|
||||||
@ -837,16 +840,23 @@ format.
|
|||||||
* Ficus https://github.com/ceedubs/ficus
|
* Ficus https://github.com/ceedubs/ficus
|
||||||
* configz https://github.com/arosien/configz
|
* configz https://github.com/arosien/configz
|
||||||
* configs https://github.com/kxbmap/configs
|
* configs https://github.com/kxbmap/configs
|
||||||
* config-annotation https://github.com/wacai/config-annotation
|
* config-annotation https://github.com/zhongl/config-annotation
|
||||||
* PureConfig https://github.com/melrief/pureconfig
|
* PureConfig https://github.com/pureconfig/pureconfig
|
||||||
* Simple Scala Config https://github.com/ElderResearch/ssc
|
* Simple Scala Config https://github.com/ElderResearch/ssc
|
||||||
* konfig https://github.com/vpon/konfig
|
* konfig https://github.com/vpon/konfig
|
||||||
* ScalaConfig https://github.com/andr83/scalaconfig
|
* ScalaConfig https://github.com/andr83/scalaconfig
|
||||||
|
* static-config https://github.com/Krever/static-config
|
||||||
|
* validated-config https://github.com/carlpulley/validated-config
|
||||||
|
* Cedi Config https://github.com/ccadllc/cedi-config
|
||||||
|
* Cfg https://github.com/carueda/cfg
|
||||||
|
|
||||||
#### Clojure wrappers for the Java library
|
#### Clojure wrappers for the Java library
|
||||||
|
|
||||||
* beamly-core.config https://github.com/beamly/beamly-core.config
|
* beamly-core.config https://github.com/beamly/beamly-core.config
|
||||||
|
|
||||||
|
#### Kotlin wrappers for the Java library
|
||||||
|
* config4k https://github.com/config4k/config4k
|
||||||
|
|
||||||
#### Scala port
|
#### Scala port
|
||||||
|
|
||||||
* SHocon https://github.com/unicredit/shocon (work with both Scala and Scala.Js)
|
* SHocon https://github.com/unicredit/shocon (work with both Scala and Scala.Js)
|
||||||
@ -867,7 +877,10 @@ format.
|
|||||||
|
|
||||||
* https://github.com/puppetlabs/cpp-hocon
|
* https://github.com/puppetlabs/cpp-hocon
|
||||||
|
|
||||||
|
#### JavaScript port
|
||||||
|
|
||||||
|
* https://github.com/yellowblood/hocon-js (missing features, under development)
|
||||||
|
|
||||||
#### Linting tool
|
#### Linting tool
|
||||||
|
|
||||||
* A web based linting tool http://www.hoconlint.com/
|
* A web based linting tool http://www.hoconlint.com/
|
||||||
|
|
||||||
|
65
build.sbt
65
build.sbt
@ -13,6 +13,58 @@ scalacOptions in GlobalScope in Test := Seq("-unchecked", "-deprecation", "-feat
|
|||||||
|
|
||||||
scalaVersion in ThisBuild := "2.10.4"
|
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
|
useGpg := true
|
||||||
|
|
||||||
aggregate in PgpKeys.publishSigned := false
|
aggregate in PgpKeys.publishSigned := false
|
||||||
@ -20,3 +72,16 @@ PgpKeys.publishSigned := (PgpKeys.publishSigned in configLib).value
|
|||||||
|
|
||||||
aggregate in PgpKeys.publishLocalSigned := false
|
aggregate in PgpKeys.publishLocalSigned := false
|
||||||
PgpKeys.publishLocalSigned := (PgpKeys.publishLocalSigned in configLib).value
|
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 := {}
|
||||||
|
)
|
||||||
|
@ -14,9 +14,13 @@ ScalariformKeys.preferences in Compile := formatPrefs
|
|||||||
ScalariformKeys.preferences in Test := formatPrefs
|
ScalariformKeys.preferences in Test := formatPrefs
|
||||||
|
|
||||||
fork in test := true
|
fork in test := true
|
||||||
|
fork in Test := true
|
||||||
fork in run := true
|
fork in run := true
|
||||||
fork in run in Test := true
|
fork in run in Test := true
|
||||||
|
|
||||||
|
//env vars for tests
|
||||||
|
envVars in Test ++= Map("testList.0" -> "0", "testList.1" -> "1")
|
||||||
|
|
||||||
autoScalaLibrary := false
|
autoScalaLibrary := false
|
||||||
crossPaths := false
|
crossPaths := false
|
||||||
|
|
||||||
@ -58,7 +62,7 @@ checkstyle in Compile := {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add checkstyle as a dependency of doc
|
// 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
|
findbugsSettings
|
||||||
findbugsReportType := Some(ReportType.Html)
|
findbugsReportType := Some(ReportType.Html)
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
package com.typesafe.config;
|
package com.typesafe.config;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Period;
|
||||||
|
import java.time.temporal.TemporalAmount;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -793,6 +795,44 @@ public interface Config extends ConfigMergeable {
|
|||||||
*/
|
*/
|
||||||
Duration getDuration(String path);
|
Duration getDuration(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a value as a java.time.Period. If the value is
|
||||||
|
* already a number, then it's taken as days; if it's
|
||||||
|
* a string, it's parsed understanding units suffixes like
|
||||||
|
* "10d" or "5w" as documented in the <a
|
||||||
|
* href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
|
||||||
|
* spec</a>. This method never returns null.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* path expression
|
||||||
|
* @return the period value at the requested path
|
||||||
|
* @throws ConfigException.Missing
|
||||||
|
* if value is absent or null
|
||||||
|
* @throws ConfigException.WrongType
|
||||||
|
* if value is not convertible to Long or String
|
||||||
|
* @throws ConfigException.BadValue
|
||||||
|
* if value cannot be parsed as a number of the given TimeUnit
|
||||||
|
*/
|
||||||
|
Period getPeriod(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a value as a java.time.temporal.TemporalAmount.
|
||||||
|
* This method will first try get get the value as a java.time.Duration, and if unsuccessful,
|
||||||
|
* then as a java.time.Period.
|
||||||
|
* This means that values like "5m" will be parsed as 5 minutes rather than 5 months
|
||||||
|
* @param path path expression
|
||||||
|
* @return the temporal value at the requested path
|
||||||
|
* @throws ConfigException.Missing
|
||||||
|
* if value is absent or null
|
||||||
|
* @throws ConfigException.WrongType
|
||||||
|
* if value is not convertible to Long or String
|
||||||
|
* @throws ConfigException.BadValue
|
||||||
|
* if value cannot be parsed as a TemporalAmount
|
||||||
|
*/
|
||||||
|
TemporalAmount getTemporal(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list value (with any element type) as a {@link ConfigList}, which
|
* Gets a list value (with any element type) as a {@link ConfigList}, which
|
||||||
* implements {@code java.util.List<ConfigValue>}. Throws if the path is
|
* implements {@code java.util.List<ConfigValue>}. Throws if the path is
|
||||||
|
@ -496,6 +496,7 @@ public final class ConfigFactory {
|
|||||||
// We rely on this having the side effect that it drops
|
// We rely on this having the side effect that it drops
|
||||||
// all caches
|
// all caches
|
||||||
ConfigImpl.reloadSystemPropertiesConfig();
|
ConfigImpl.reloadSystemPropertiesConfig();
|
||||||
|
ConfigImpl.reloadEnvVariablesConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,10 +29,13 @@ package com.typesafe.config;
|
|||||||
public final class ConfigResolveOptions {
|
public final class ConfigResolveOptions {
|
||||||
private final boolean useSystemEnvironment;
|
private final boolean useSystemEnvironment;
|
||||||
private final boolean allowUnresolved;
|
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.useSystemEnvironment = useSystemEnvironment;
|
||||||
this.allowUnresolved = allowUnresolved;
|
this.allowUnresolved = allowUnresolved;
|
||||||
|
this.resolver = resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +45,7 @@ public final class ConfigResolveOptions {
|
|||||||
* @return the default resolve options
|
* @return the default resolve options
|
||||||
*/
|
*/
|
||||||
public static ConfigResolveOptions defaults() {
|
public static ConfigResolveOptions defaults() {
|
||||||
return new ConfigResolveOptions(true, false);
|
return new ConfigResolveOptions(true, false, NULL_RESOLVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +67,7 @@ public final class ConfigResolveOptions {
|
|||||||
* @return options with requested setting for use of environment variables
|
* @return options with requested setting for use of environment variables
|
||||||
*/
|
*/
|
||||||
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
|
||||||
return new ConfigResolveOptions(value, allowUnresolved);
|
return new ConfigResolveOptions(value, allowUnresolved, resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +94,55 @@ public final class ConfigResolveOptions {
|
|||||||
* @since 1.2.0
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
public ConfigResolveOptions setAllowUnresolved(boolean value) {
|
public ConfigResolveOptions setAllowUnresolved(boolean value) {
|
||||||
return new ConfigResolveOptions(useSystemEnvironment, value);
|
return new ConfigResolveOptions(useSystemEnvironment, value, resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns options where the given resolver used as a fallback if a
|
||||||
|
* reference cannot be otherwise resolved. This resolver will only be called
|
||||||
|
* after resolution has failed to substitute with a value from within the
|
||||||
|
* config itself and with any other resolvers that have been appended before
|
||||||
|
* this one. Multiple resolvers can be added using,
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ConfigResolveOptions options = ConfigResolveOptions.defaults()
|
||||||
|
* .appendResolver(primary)
|
||||||
|
* .appendResolver(secondary)
|
||||||
|
* .appendResolver(tertiary);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* With this config unresolved references will first be resolved with the
|
||||||
|
* primary resolver, if that fails then the secondary, and finally if that
|
||||||
|
* also fails the tertiary.
|
||||||
|
*
|
||||||
|
* If all fallbacks fail to return a substitution "allow unresolved"
|
||||||
|
* determines whether resolution fails or continues.
|
||||||
|
*`
|
||||||
|
* @param value the resolver to fall back to
|
||||||
|
* @return options that use the given resolver as a fallback
|
||||||
|
* @since 1.3.2
|
||||||
|
*/
|
||||||
|
public ConfigResolveOptions appendResolver(ConfigResolver value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new ConfigException.BugOrBroken("null resolver passed to appendResolver");
|
||||||
|
} else if (value == this.resolver) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return new ConfigResolveOptions(useSystemEnvironment, allowUnresolved,
|
||||||
|
this.resolver.withFallback(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resolver to use as a fallback if a substitution cannot be
|
||||||
|
* otherwise resolved. Never returns null. This method is mostly used by the
|
||||||
|
* config lib internally, not by applications.
|
||||||
|
*
|
||||||
|
* @return the non-null fallback resolver
|
||||||
|
* @since 1.3.2
|
||||||
|
*/
|
||||||
|
public ConfigResolver getResolver() {
|
||||||
|
return this.resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,4 +155,22 @@ public final class ConfigResolveOptions {
|
|||||||
public boolean getAllowUnresolved() {
|
public boolean getAllowUnresolved() {
|
||||||
return allowUnresolved;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
38
config/src/main/java/com/typesafe/config/ConfigResolver.java
Normal file
38
config/src/main/java/com/typesafe/config/ConfigResolver.java
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -11,9 +11,11 @@ import java.lang.reflect.ParameterizedType;
|
|||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.typesafe.config.Config;
|
||||||
import com.typesafe.config.ConfigObject;
|
import com.typesafe.config.ConfigObject;
|
||||||
@ -160,6 +162,8 @@ public class ConfigBeanImpl {
|
|||||||
return config.getAnyRef(configPropName);
|
return config.getAnyRef(configPropName);
|
||||||
} else if (parameterClass == List.class) {
|
} else if (parameterClass == List.class) {
|
||||||
return getListValue(beanClass, parameterType, parameterClass, config, configPropName);
|
return getListValue(beanClass, parameterType, parameterClass, config, configPropName);
|
||||||
|
} else if (parameterClass == Set.class) {
|
||||||
|
return getSetValue(beanClass, parameterType, parameterClass, config, configPropName);
|
||||||
} else if (parameterClass == Map.class) {
|
} else if (parameterClass == Map.class) {
|
||||||
// we could do better here, but right now we don't.
|
// we could do better here, but right now we don't.
|
||||||
Type[] typeArgs = ((ParameterizedType)parameterType).getActualTypeArguments();
|
Type[] typeArgs = ((ParameterizedType)parameterType).getActualTypeArguments();
|
||||||
@ -186,6 +190,10 @@ public class ConfigBeanImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
private static Object getListValue(Class<?> beanClass, Type parameterType, Class<?> parameterClass, Config config, String configPropName) {
|
||||||
Type elementType = ((ParameterizedType)parameterType).getActualTypeArguments()[0];
|
Type elementType = ((ParameterizedType)parameterType).getActualTypeArguments()[0];
|
||||||
|
|
||||||
@ -277,7 +285,7 @@ public class ConfigBeanImpl {
|
|||||||
|
|
||||||
private static boolean isOptionalProperty(Class beanClass, PropertyDescriptor beanProp) {
|
private static boolean isOptionalProperty(Class beanClass, PropertyDescriptor beanProp) {
|
||||||
Field field = getField(beanClass, beanProp.getName());
|
Field field = getField(beanClass, beanProp.getName());
|
||||||
return (field.getAnnotationsByType(Optional.class).length > 0);
|
return field != null && (field.getAnnotationsByType(Optional.class).length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Field getField(Class beanClass, String fieldName) {
|
private static Field getField(Class beanClass, String fieldName) {
|
||||||
|
@ -335,20 +335,11 @@ public class ConfigImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static AbstractConfigObject loadEnvVariables() {
|
private static AbstractConfigObject loadEnvVariables() {
|
||||||
Map<String, String> env = System.getenv();
|
return PropertiesParser.fromStringMap(newSimpleOrigin("env variables"), System.getenv());
|
||||||
Map<String, AbstractConfigValue> m = new HashMap<String, AbstractConfigValue>();
|
|
||||||
for (Map.Entry<String, String> entry : env.entrySet()) {
|
|
||||||
String key = entry.getKey();
|
|
||||||
m.put(key,
|
|
||||||
new ConfigString.Quoted(SimpleConfigOrigin.newSimple("env var " + key), entry
|
|
||||||
.getValue()));
|
|
||||||
}
|
|
||||||
return new SimpleConfigObject(SimpleConfigOrigin.newSimple("env variables"),
|
|
||||||
m, ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EnvVariablesHolder {
|
private static class EnvVariablesHolder {
|
||||||
static final AbstractConfigObject envVariables = loadEnvVariables();
|
static volatile AbstractConfigObject envVariables = loadEnvVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
static AbstractConfigObject envVariablesAsConfigObject() {
|
static AbstractConfigObject envVariablesAsConfigObject() {
|
||||||
@ -363,6 +354,12 @@ public class ConfigImpl {
|
|||||||
return envVariablesAsConfigObject().toConfig();
|
return envVariablesAsConfigObject().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void reloadEnvVariablesConfig() {
|
||||||
|
// ConfigFactory.invalidateCaches() relies on this having the side
|
||||||
|
// effect that it drops all caches
|
||||||
|
EnvVariablesHolder.envVariables = loadEnvVariables();
|
||||||
|
}
|
||||||
|
|
||||||
public static Config defaultReference(final ClassLoader loader) {
|
public static Config defaultReference(final ClassLoader loader) {
|
||||||
return computeCachedConfig(loader, "defaultReference", new Callable<Config>() {
|
return computeCachedConfig(loader, "defaultReference", new Callable<Config>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -6,6 +6,8 @@ import java.util.Collections;
|
|||||||
import com.typesafe.config.ConfigException;
|
import com.typesafe.config.ConfigException;
|
||||||
import com.typesafe.config.ConfigOrigin;
|
import com.typesafe.config.ConfigOrigin;
|
||||||
import com.typesafe.config.ConfigRenderOptions;
|
import com.typesafe.config.ConfigRenderOptions;
|
||||||
|
import com.typesafe.config.ConfigResolveOptions;
|
||||||
|
import com.typesafe.config.ConfigValue;
|
||||||
import com.typesafe.config.ConfigValueType;
|
import com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,7 +90,8 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
|||||||
v = result.value;
|
v = result.value;
|
||||||
newContext = result.context;
|
newContext = result.context;
|
||||||
} else {
|
} else {
|
||||||
v = null;
|
ConfigValue fallback = context.options().getResolver().lookup(expr.path().render());
|
||||||
|
v = (AbstractConfigValue) fallback;
|
||||||
}
|
}
|
||||||
} catch (NotPossibleToResolve e) {
|
} catch (NotPossibleToResolve e) {
|
||||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
|
@ -56,15 +56,28 @@ final class PropertiesParser {
|
|||||||
|
|
||||||
static AbstractConfigObject fromProperties(ConfigOrigin origin,
|
static AbstractConfigObject fromProperties(ConfigOrigin origin,
|
||||||
Properties props) {
|
Properties props) {
|
||||||
|
return fromEntrySet(origin, props.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <K, V> AbstractConfigObject fromEntrySet(ConfigOrigin origin, Set<Map.Entry<K, V>> entries) {
|
||||||
|
final Map<Path, Object> pathMap = getPathMap(entries);
|
||||||
|
return fromPathMap(origin, pathMap, true /* from properties */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <K, V> Map<Path, Object> getPathMap(Set<Map.Entry<K, V>> entries) {
|
||||||
Map<Path, Object> pathMap = new HashMap<Path, Object>();
|
Map<Path, Object> pathMap = new HashMap<Path, Object>();
|
||||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
for (Map.Entry<K, V> entry : entries) {
|
||||||
Object key = entry.getKey();
|
Object key = entry.getKey();
|
||||||
if (key instanceof String) {
|
if (key instanceof String) {
|
||||||
Path path = pathFromPropertyKey((String) key);
|
Path path = pathFromPropertyKey((String) key);
|
||||||
pathMap.put(path, entry.getValue());
|
pathMap.put(path, entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fromPathMap(origin, pathMap, true /* from properties */);
|
return pathMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AbstractConfigObject fromStringMap(ConfigOrigin origin, Map<String, String> stringMap) {
|
||||||
|
return fromEntrySet(origin, stringMap.entrySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
static AbstractConfigObject fromPathMap(ConfigOrigin origin,
|
static AbstractConfigObject fromPathMap(ConfigOrigin origin,
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.Externalizable;
|
import java.io.Externalizable;
|
||||||
@ -465,19 +467,23 @@ class SerializedConfigValue extends AbstractConfigValue implements Externalizabl
|
|||||||
SerializedField code = readCode(in);
|
SerializedField code = readCode(in);
|
||||||
if (code == SerializedField.END_MARKER) {
|
if (code == SerializedField.END_MARKER) {
|
||||||
return;
|
return;
|
||||||
} else if (code == SerializedField.ROOT_VALUE) {
|
}
|
||||||
in.readInt(); // discard length
|
|
||||||
this.value = readValue(in, null /* baseOrigin */);
|
DataInput input = fieldIn(in);
|
||||||
|
if (code == SerializedField.ROOT_VALUE) {
|
||||||
|
this.value = readValue(input, null /* baseOrigin */);
|
||||||
} else if (code == SerializedField.ROOT_WAS_CONFIG) {
|
} else if (code == SerializedField.ROOT_WAS_CONFIG) {
|
||||||
in.readInt(); // discard length
|
this.wasConfig = input.readBoolean();
|
||||||
this.wasConfig = in.readBoolean();
|
|
||||||
} else {
|
|
||||||
// ignore unknown field
|
|
||||||
skipField(in);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DataInput fieldIn(ObjectInput in) throws IOException {
|
||||||
|
byte[] bytes = new byte[in.readInt()];
|
||||||
|
in.readFully(bytes);
|
||||||
|
return new DataInputStream(new ByteArrayInputStream(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
private static ConfigException shouldNotBeUsed() {
|
private static ConfigException shouldNotBeUsed() {
|
||||||
return new ConfigException.BugOrBroken(SerializedConfigValue.class.getName()
|
return new ConfigException.BugOrBroken(SerializedConfigValue.class.getName()
|
||||||
+ " should not exist outside of serialization");
|
+ " should not exist outside of serialization");
|
||||||
|
@ -7,7 +7,11 @@ import java.io.ObjectStreamException;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.time.DateTimeException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Period;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.time.temporal.TemporalAmount;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -322,6 +326,21 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
|||||||
return Duration.ofNanos(nanos);
|
return Duration.ofNanos(nanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Period getPeriod(String path){
|
||||||
|
ConfigValue v = find(path, ConfigValueType.STRING);
|
||||||
|
return parsePeriod((String) v.unwrapped(), v.origin(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TemporalAmount getTemporal(String path){
|
||||||
|
try{
|
||||||
|
return getDuration(path);
|
||||||
|
} catch (ConfigException.BadValue e){
|
||||||
|
return getPeriod(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> List<T> getHomogeneousUnwrappedList(String path,
|
private <T> List<T> getHomogeneousUnwrappedList(String path,
|
||||||
ConfigValueType expected) {
|
ConfigValueType expected) {
|
||||||
@ -583,6 +602,90 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
|
|||||||
return s.substring(i + 1);
|
return s.substring(i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a period string. If no units are specified in the string, it is
|
||||||
|
* assumed to be in days. The returned period is in days.
|
||||||
|
* The purpose of this function is to implement the period-related methods
|
||||||
|
* in the ConfigObject interface.
|
||||||
|
*
|
||||||
|
* @param input
|
||||||
|
* the string to parse
|
||||||
|
* @param originForException
|
||||||
|
* origin of the value being parsed
|
||||||
|
* @param pathForException
|
||||||
|
* path to include in exceptions
|
||||||
|
* @return duration in days
|
||||||
|
* @throws ConfigException
|
||||||
|
* if string is invalid
|
||||||
|
*/
|
||||||
|
public static Period parsePeriod(String input,
|
||||||
|
ConfigOrigin originForException, String pathForException) {
|
||||||
|
String s = ConfigImplUtil.unicodeTrim(input);
|
||||||
|
String originalUnitString = getUnits(s);
|
||||||
|
String unitString = originalUnitString;
|
||||||
|
String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, s.length()
|
||||||
|
- unitString.length()));
|
||||||
|
ChronoUnit units;
|
||||||
|
|
||||||
|
// this would be caught later anyway, but the error message
|
||||||
|
// is more helpful if we check it here.
|
||||||
|
if (numberString.length() == 0)
|
||||||
|
throw new ConfigException.BadValue(originForException,
|
||||||
|
pathForException, "No number in period value '" + input
|
||||||
|
+ "'");
|
||||||
|
|
||||||
|
if (unitString.length() > 2 && !unitString.endsWith("s"))
|
||||||
|
unitString = unitString + "s";
|
||||||
|
|
||||||
|
// note that this is deliberately case-sensitive
|
||||||
|
if (unitString.equals("") || unitString.equals("d") || unitString.equals("days")) {
|
||||||
|
units = ChronoUnit.DAYS;
|
||||||
|
|
||||||
|
} else if (unitString.equals("w") || unitString.equals("weeks")) {
|
||||||
|
units = ChronoUnit.WEEKS;
|
||||||
|
|
||||||
|
} else if (unitString.equals("m") || unitString.equals("mo") || unitString.equals("months")) {
|
||||||
|
units = ChronoUnit.MONTHS;
|
||||||
|
|
||||||
|
} else if (unitString.equals("y") || unitString.equals("years")) {
|
||||||
|
units = ChronoUnit.YEARS;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new ConfigException.BadValue(originForException,
|
||||||
|
pathForException, "Could not parse time unit '"
|
||||||
|
+ originalUnitString
|
||||||
|
+ "' (try d, w, mo, y)");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return periodOf(Integer.parseInt(numberString), units);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new ConfigException.BadValue(originForException,
|
||||||
|
pathForException, "Could not parse duration number '"
|
||||||
|
+ numberString + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Period periodOf(int n, ChronoUnit unit){
|
||||||
|
if(unit.isTimeBased()){
|
||||||
|
throw new DateTimeException(unit + " cannot be converted to a java.time.Period");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (unit){
|
||||||
|
case DAYS:
|
||||||
|
return Period.ofDays(n);
|
||||||
|
case WEEKS:
|
||||||
|
return Period.ofWeeks(n);
|
||||||
|
case MONTHS:
|
||||||
|
return Period.ofMonths(n);
|
||||||
|
case YEARS:
|
||||||
|
return Period.ofYears(n);
|
||||||
|
default:
|
||||||
|
throw new DateTimeException(unit + " cannot be converted to a java.time.Period");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a duration string. If no units are specified in the string, it is
|
* Parses a duration string. If no units are specified in the string, it is
|
||||||
* assumed to be in milliseconds. The returned duration is in nanoseconds.
|
* assumed to be in milliseconds. The returned duration is in nanoseconds.
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package beanconfig;
|
||||||
|
|
||||||
|
public class DifferentFieldNameFromAccessorsConfig {
|
||||||
|
|
||||||
|
private String customStringField;
|
||||||
|
private Long number;
|
||||||
|
|
||||||
|
|
||||||
|
public String getStringField() {
|
||||||
|
return customStringField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStringField(String stringField) {
|
||||||
|
this.customStringField = stringField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(Long number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
}
|
139
config/src/test/java/beanconfig/SetsConfig.java
Normal file
139
config/src/test/java/beanconfig/SetsConfig.java
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package beanconfig;
|
||||||
|
|
||||||
|
import com.typesafe.config.Config;
|
||||||
|
import com.typesafe.config.ConfigMemorySize;
|
||||||
|
import com.typesafe.config.ConfigObject;
|
||||||
|
import com.typesafe.config.ConfigValue;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class SetsConfig {
|
||||||
|
|
||||||
|
Set<Integer> empty;
|
||||||
|
Set<Integer> ofInt;
|
||||||
|
Set<String> ofString;
|
||||||
|
Set<Double> ofDouble;
|
||||||
|
Set<Long> ofLong;
|
||||||
|
Set<Object> ofNull;
|
||||||
|
Set<Boolean> ofBoolean;
|
||||||
|
Set<Object> ofObject;
|
||||||
|
Set<Config> ofConfig;
|
||||||
|
Set<ConfigObject> ofConfigObject;
|
||||||
|
Set<ConfigValue> ofConfigValue;
|
||||||
|
Set<Duration> ofDuration;
|
||||||
|
Set<ConfigMemorySize> ofMemorySize;
|
||||||
|
Set<StringsConfig> ofStringBean;
|
||||||
|
|
||||||
|
public Set<Integer> getEmpty() {
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmpty(Set<Integer> empty) {
|
||||||
|
this.empty = empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getOfInt() {
|
||||||
|
return ofInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfInt(Set<Integer> ofInt) {
|
||||||
|
this.ofInt = ofInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getOfString() {
|
||||||
|
return ofString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfString(Set<String> ofString) {
|
||||||
|
this.ofString = ofString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Double> getOfDouble() {
|
||||||
|
return ofDouble;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfDouble(Set<Double> ofDouble) {
|
||||||
|
this.ofDouble = ofDouble;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getOfNull() {
|
||||||
|
return ofNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfNull(Set<Object> ofNull) {
|
||||||
|
this.ofNull = ofNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Boolean> getOfBoolean() {
|
||||||
|
return ofBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfBoolean(Set<Boolean> ofBoolean) {
|
||||||
|
this.ofBoolean = ofBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getOfObject() {
|
||||||
|
return ofObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfObject(Set<Object> ofObject) {
|
||||||
|
this.ofObject = ofObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getOfLong() {
|
||||||
|
return ofLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfLong(Set<Long> ofLong) {
|
||||||
|
this.ofLong = ofLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Config> getOfConfig() {
|
||||||
|
return ofConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfConfig(Set<Config> ofConfig) {
|
||||||
|
this.ofConfig = ofConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ConfigObject> getOfConfigObject() {
|
||||||
|
return ofConfigObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfConfigObject(Set<ConfigObject> ofConfigObject) {
|
||||||
|
this.ofConfigObject = ofConfigObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ConfigValue> getOfConfigValue() {
|
||||||
|
return ofConfigValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfConfigValue(Set<ConfigValue> ofConfigValue) {
|
||||||
|
this.ofConfigValue = ofConfigValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Duration> getOfDuration() {
|
||||||
|
return ofDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfDuration(Set<Duration> ofDuration) {
|
||||||
|
this.ofDuration = ofDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ConfigMemorySize> getOfMemorySize() {
|
||||||
|
return ofMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfMemorySize(Set<ConfigMemorySize> ofMemorySize) {
|
||||||
|
this.ofMemorySize = ofMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<StringsConfig> getOfStringBean() {
|
||||||
|
return ofStringBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfStringBean(Set<StringsConfig> ofStringBean) {
|
||||||
|
this.ofStringBean = ofStringBean;
|
||||||
|
}
|
||||||
|
}
|
@ -101,5 +101,31 @@
|
|||||||
"valueObject": {
|
"valueObject": {
|
||||||
"mandatoryValue": "notNull"
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,14 @@
|
|||||||
"minusLargeNanos" : -4878955355435272204ns
|
"minusLargeNanos" : -4878955355435272204ns
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"periods" : {
|
||||||
|
"day" : 1d,
|
||||||
|
"dayAsNumber": 2,
|
||||||
|
"week": 3 weeks,
|
||||||
|
"month": 5 mo,
|
||||||
|
"year": 8y
|
||||||
|
},
|
||||||
|
|
||||||
"memsizes" : {
|
"memsizes" : {
|
||||||
"meg" : 1M,
|
"meg" : 1M,
|
||||||
"megsList" : [1M, 1024K, 1048576],
|
"megsList" : [1M, 1024K, 1048576],
|
||||||
|
@ -132,6 +132,39 @@ class ConfigBeanFactoryTest extends TestUtils {
|
|||||||
assertEquals(List(stringsConfigOne, stringsConfigTwo).asJava, beanConfig.getOfStringBean)
|
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
|
@Test
|
||||||
def testCreateDuration() {
|
def testCreateDuration() {
|
||||||
val beanConfig: DurationsConfig = ConfigBeanFactory.create(loadConfig().getConfig("durations"), classOf[DurationsConfig])
|
val beanConfig: DurationsConfig = ConfigBeanFactory.create(loadConfig().getConfig("durations"), classOf[DurationsConfig])
|
||||||
@ -237,6 +270,14 @@ class ConfigBeanFactoryTest extends TestUtils {
|
|||||||
assertTrue("error about the right property", e.getMessage.contains("'map'"))
|
assertTrue("error about the right property", e.getMessage.contains("'map'"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def testDifferentFieldNameFromAccessors(): Unit = {
|
||||||
|
val e = intercept[ConfigException.ValidationFailed] {
|
||||||
|
ConfigBeanFactory.create(ConfigFactory.empty(), classOf[DifferentFieldNameFromAccessorsConfig])
|
||||||
|
}
|
||||||
|
assertTrue("only one missing value error", e.getMessage.contains("No setting"))
|
||||||
|
}
|
||||||
|
|
||||||
private def loadConfig(): Config = {
|
private def loadConfig(): Config = {
|
||||||
val configIs: InputStream = this.getClass().getClassLoader().getResourceAsStream("beanconfig/beanconfig01.conf")
|
val configIs: InputStream = this.getClass().getClassLoader().getResourceAsStream("beanconfig/beanconfig01.conf")
|
||||||
try {
|
try {
|
||||||
|
@ -292,7 +292,7 @@ class ConfigDocumentTest extends TestUtils {
|
|||||||
@Test
|
@Test
|
||||||
def configDocumentFileParse {
|
def configDocumentFileParse {
|
||||||
val configDocument = ConfigDocumentFactory.parseFile(resourceFile("/test03.conf"))
|
val configDocument = ConfigDocumentFactory.parseFile(resourceFile("/test03.conf"))
|
||||||
val fileReader = new BufferedReader(new FileReader("config/src/test/resources/test03.conf"))
|
val fileReader = new BufferedReader(new FileReader("src/test/resources/test03.conf"))
|
||||||
var line = fileReader.readLine()
|
var line = fileReader.readLine()
|
||||||
val sb = new StringBuilder()
|
val sb = new StringBuilder()
|
||||||
while (line != null) {
|
while (line != null) {
|
||||||
|
@ -10,6 +10,7 @@ import com.typesafe.config.ConfigException
|
|||||||
import com.typesafe.config.ConfigResolveOptions
|
import com.typesafe.config.ConfigResolveOptions
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
class ConfigSubstitutionTest extends TestUtils {
|
class ConfigSubstitutionTest extends TestUtils {
|
||||||
|
|
||||||
@ -723,6 +724,35 @@ class ConfigSubstitutionTest extends TestUtils {
|
|||||||
checkNotSerializable(substComplexObject)
|
checkNotSerializable(substComplexObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def resolveListFromSystemProps() {
|
||||||
|
val props = parseObject(
|
||||||
|
"""
|
||||||
|
|"a": ${testList}
|
||||||
|
""".stripMargin)
|
||||||
|
|
||||||
|
System.setProperty("testList.0", "0")
|
||||||
|
System.setProperty("testList.1", "1")
|
||||||
|
ConfigImpl.reloadSystemPropertiesConfig()
|
||||||
|
|
||||||
|
val resolved = resolve(ConfigFactory.systemProperties().withFallback(props).root.asInstanceOf[AbstractConfigObject])
|
||||||
|
|
||||||
|
assertEquals(List("0", "1"), resolved.getList("a").unwrapped().asScala)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def resolveListFromEnvVars() {
|
||||||
|
val props = parseObject(
|
||||||
|
"""
|
||||||
|
|"a": ${testList}
|
||||||
|
""".stripMargin)
|
||||||
|
|
||||||
|
//"testList.0" and "testList.1" are defined as envVars in build.sbt
|
||||||
|
val resolved = resolve(props)
|
||||||
|
|
||||||
|
assertEquals(List("0", "1"), resolved.getList("a").unwrapped().asScala)
|
||||||
|
}
|
||||||
|
|
||||||
// this is a weird test, it used to test fallback to system props which made more sense.
|
// this is a weird test, it used to test fallback to system props which made more sense.
|
||||||
// Now it just tests that if you override with system props, you can use system props
|
// Now it just tests that if you override with system props, you can use system props
|
||||||
// in substitutions.
|
// in substitutions.
|
||||||
|
@ -3,13 +3,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl
|
package com.typesafe.config.impl
|
||||||
|
|
||||||
|
import java.time.temporal.{ ChronoUnit, TemporalUnit }
|
||||||
|
|
||||||
import org.junit.Assert._
|
import org.junit.Assert._
|
||||||
import org.junit._
|
import org.junit._
|
||||||
import com.typesafe.config._
|
import com.typesafe.config._
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import com.typesafe.config.ConfigResolveOptions
|
import com.typesafe.config.ConfigResolveOptions
|
||||||
import java.util.concurrent.TimeUnit.{ SECONDS, NANOSECONDS, MICROSECONDS, MILLISECONDS, MINUTES, DAYS, HOURS }
|
import java.util.concurrent.TimeUnit.{ DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS }
|
||||||
|
|
||||||
class ConfigTest extends TestUtils {
|
class ConfigTest extends TestUtils {
|
||||||
|
|
||||||
@ -811,6 +814,13 @@ class ConfigTest extends TestUtils {
|
|||||||
assertDurationAsTimeUnit(HOURS)
|
assertDurationAsTimeUnit(HOURS)
|
||||||
assertDurationAsTimeUnit(DAYS)
|
assertDurationAsTimeUnit(DAYS)
|
||||||
|
|
||||||
|
// periods
|
||||||
|
assertEquals(1, conf.getPeriod("periods.day").get(ChronoUnit.DAYS))
|
||||||
|
assertEquals(2, conf.getPeriod("periods.dayAsNumber").getDays)
|
||||||
|
assertEquals(3 * 7, conf.getTemporal("periods.week").get(ChronoUnit.DAYS))
|
||||||
|
assertEquals(5, conf.getTemporal("periods.month").get(ChronoUnit.MONTHS))
|
||||||
|
assertEquals(8, conf.getTemporal("periods.year").get(ChronoUnit.YEARS))
|
||||||
|
|
||||||
// should get size in bytes
|
// should get size in bytes
|
||||||
assertEquals(1024 * 1024L, conf.getBytes("memsizes.meg"))
|
assertEquals(1024 * 1024L, conf.getBytes("memsizes.meg"))
|
||||||
assertEquals(1024 * 1024L, conf.getBytes("memsizes.megAsNumber"))
|
assertEquals(1024 * 1024L, conf.getBytes("memsizes.megAsNumber"))
|
||||||
@ -1233,4 +1243,70 @@ class ConfigTest extends TestUtils {
|
|||||||
val resolved = unresolved.resolveWith(source)
|
val resolved = unresolved.resolveWith(source)
|
||||||
assertEquals(43, resolved.getInt("foo"))
|
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}"))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -279,6 +279,23 @@ class ConfigValueTest extends TestUtils {
|
|||||||
assertTrue(b.root.toConfig eq b)
|
assertTrue(b.root.toConfig eq b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reproduces the issue <a href=https://github.com/typesafehub/config/issues/461>#461</a>.
|
||||||
|
* <p>
|
||||||
|
* We use a custom de-/serializer that encodes String objects in a JDK-incompatible way. Encoding used here
|
||||||
|
* is rather simplistic: a long indicating the length in bytes (JDK uses a variable length integer) followed
|
||||||
|
* by the string's bytes. Running this test with the original SerializedConfigValue.readExternal()
|
||||||
|
* implementation results in an EOFException thrown during deserialization.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
def configConfigCustomSerializable() {
|
||||||
|
val aMap = configMap("a" -> 1, "b" -> 2, "c" -> 3)
|
||||||
|
val expected = new SimpleConfigObject(fakeOrigin(), aMap).toConfig
|
||||||
|
val actual = checkSerializableWithCustomSerializer(expected)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def configListEquality() {
|
def configListEquality() {
|
||||||
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
|
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
|
||||||
|
@ -7,8 +7,7 @@ import org.junit.Assert._
|
|||||||
import org.junit._
|
import org.junit._
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import com.typesafe.config._
|
import com.typesafe.config._
|
||||||
import java.util.Collections
|
import java.util.{ Collections, TimeZone, TreeSet }
|
||||||
import java.util.TreeSet
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import equiv03.SomethingInEquiv03
|
import equiv03.SomethingInEquiv03
|
||||||
@ -17,6 +16,15 @@ import java.net.URL
|
|||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
class PublicApiTest extends TestUtils {
|
class PublicApiTest extends TestUtils {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
def before(): Unit = {
|
||||||
|
// TimeZone.getDefault internally invokes System.setProperty("user.timezone", <default time zone>) and it may
|
||||||
|
// cause flaky tests depending on tests order and jvm options. This method is invoked
|
||||||
|
// eg. by URLConnection.getContentType (it reads headers and gets default time zone).
|
||||||
|
TimeZone.getDefault
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def basicLoadAndGet() {
|
def basicLoadAndGet() {
|
||||||
val conf = ConfigFactory.load("test01")
|
val conf = ConfigFactory.load("test01")
|
||||||
@ -1016,8 +1024,8 @@ class PublicApiTest extends TestUtils {
|
|||||||
assertTrue("invalidate caches works on changed system props sys", sys2 ne sys3)
|
assertTrue("invalidate caches works on changed system props sys", sys2 ne sys3)
|
||||||
assertTrue("invalidate caches works on changed system props conf", conf2 ne conf3)
|
assertTrue("invalidate caches works on changed system props conf", conf2 ne conf3)
|
||||||
|
|
||||||
assertTrue("invalidate caches doesn't change value if no system prop changes sys", sys1 == sys2)
|
assertEquals("invalidate caches doesn't change value if no system prop changes sys", sys1, sys2)
|
||||||
assertTrue("invalidate caches doesn't change value if no system prop changes conf", conf1 == conf2)
|
assertEquals("invalidate caches doesn't change value if no system prop changes conf", conf1, conf2)
|
||||||
|
|
||||||
assertTrue("test system property is set sys", sys3.hasPath("invalidateCachesTest"))
|
assertTrue("test system property is set sys", sys3.hasPath("invalidateCachesTest"))
|
||||||
assertTrue("test system property is set conf", conf3.hasPath("invalidateCachesTest"))
|
assertTrue("test system property is set conf", conf3.hasPath("invalidateCachesTest"))
|
||||||
|
@ -16,6 +16,8 @@ import java.io.ObjectOutputStream
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ObjectInputStream
|
import java.io.ObjectInputStream
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.io.InputStream
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@ -721,7 +723,7 @@ abstract trait TestUtils {
|
|||||||
def path(elements: String*) = new Path(elements: _*)
|
def path(elements: String*) = new Path(elements: _*)
|
||||||
|
|
||||||
val resourceDir = {
|
val resourceDir = {
|
||||||
val f = new File("config/src/test/resources")
|
val f = new File("src/test/resources")
|
||||||
if (!f.exists()) {
|
if (!f.exists()) {
|
||||||
val here = new File(".").getAbsolutePath
|
val here = new File(".").getAbsolutePath
|
||||||
throw new Exception(s"Tests must be run from the root project directory containing ${f.getPath()}, however the current directory is $here")
|
throw new Exception(s"Tests must be run from the root project directory containing ${f.getPath()}, however the current directory is $here")
|
||||||
@ -871,7 +873,7 @@ abstract trait TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected def withScratchDirectory[T](testcase: String)(body: File => T): Unit = {
|
protected def withScratchDirectory[T](testcase: String)(body: File => T): Unit = {
|
||||||
val target = new File("config/target")
|
val target = new File("target")
|
||||||
if (!target.isDirectory)
|
if (!target.isDirectory)
|
||||||
throw new RuntimeException(s"Expecting $target to exist")
|
throw new RuntimeException(s"Expecting $target to exist")
|
||||||
val suffix = java.lang.Integer.toHexString(java.util.concurrent.ThreadLocalRandom.current.nextInt)
|
val suffix = java.lang.Integer.toHexString(java.util.concurrent.ThreadLocalRandom.current.nextInt)
|
||||||
@ -883,4 +885,32 @@ abstract trait TestUtils {
|
|||||||
deleteRecursive(scratch)
|
deleteRecursive(scratch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected def checkSerializableWithCustomSerializer[T: Manifest](o: T): T = {
|
||||||
|
val byteStream = new ByteArrayOutputStream()
|
||||||
|
val objectStream = new CustomObjectOutputStream(byteStream)
|
||||||
|
objectStream.writeObject(o)
|
||||||
|
objectStream.close()
|
||||||
|
val inStream = new ByteArrayInputStream(byteStream.toByteArray)
|
||||||
|
val inObjectStream = new CustomObjectInputStream(inStream)
|
||||||
|
val copy = inObjectStream.readObject()
|
||||||
|
inObjectStream.close()
|
||||||
|
copy.asInstanceOf[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomObjectOutputStream(out: OutputStream) extends ObjectOutputStream(out) {
|
||||||
|
override def writeUTF(str: String): Unit = {
|
||||||
|
val bytes = str.getBytes
|
||||||
|
writeLong(bytes.length)
|
||||||
|
write(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomObjectInputStream(in: InputStream) extends ObjectInputStream(in) {
|
||||||
|
override def readUTF(): String = {
|
||||||
|
val bytes = new Array[Byte](readLong().toByte)
|
||||||
|
read(bytes)
|
||||||
|
new String(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl
|
package com.typesafe.config.impl
|
||||||
|
|
||||||
|
import java.time.{ LocalDate, Period }
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
import org.junit.Assert._
|
import org.junit.Assert._
|
||||||
import org.junit._
|
import org.junit._
|
||||||
import com.typesafe.config._
|
import com.typesafe.config._
|
||||||
@ -40,6 +43,33 @@ class UnitParserTest extends TestUtils {
|
|||||||
assertTrue(e2.getMessage.contains("duration number"))
|
assertTrue(e2.getMessage.contains("duration number"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def parsePeriod() = {
|
||||||
|
val oneYears = List(
|
||||||
|
"1y", "1 y", "1year", "1 years", " 1y ", " 1 y ",
|
||||||
|
"365", "365d", "365 d", "365 days", " 365 days ", "365day",
|
||||||
|
"12m", "12mo", "12 m", " 12 mo ", "12 months", "12month")
|
||||||
|
val epochDate = LocalDate.ofEpochDay(0)
|
||||||
|
val oneYear = ChronoUnit.DAYS.between(epochDate, epochDate.plus(Period.ofYears(1)))
|
||||||
|
for (y <- oneYears) {
|
||||||
|
val period = SimpleConfig.parsePeriod(y, fakeOrigin(), "test")
|
||||||
|
val dayCount = ChronoUnit.DAYS.between(epochDate, epochDate.plus(period))
|
||||||
|
assertEquals(oneYear, dayCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad units
|
||||||
|
val e = intercept[ConfigException.BadValue] {
|
||||||
|
SimpleConfig.parsePeriod("100 dollars", fakeOrigin(), "test")
|
||||||
|
}
|
||||||
|
assertTrue(s"${e.getMessage} was not the expected error message", e.getMessage.contains("time unit"))
|
||||||
|
|
||||||
|
// bad number
|
||||||
|
val e2 = intercept[ConfigException.BadValue] {
|
||||||
|
SimpleConfig.parsePeriod("1 00 seconds", fakeOrigin(), "test")
|
||||||
|
}
|
||||||
|
assertTrue(s"${e2.getMessage} was not the expected error message", e2.getMessage.contains("time unit 'seconds'"))
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/typesafehub/config/issues/117
|
// https://github.com/typesafehub/config/issues/117
|
||||||
// this broke because "1d" is a valid double for parseDouble
|
// this broke because "1d" is a valid double for parseDouble
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
import sbt._
|
|
||||||
import Keys._
|
|
||||||
import com.etsy.sbt.checkstyle.CheckstylePlugin.autoImport._
|
|
||||||
import com.typesafe.sbt.osgi.SbtOsgi
|
|
||||||
import com.typesafe.sbt.osgi.SbtOsgi.autoImport._
|
|
||||||
import com.typesafe.sbt.JavaVersionCheckPlugin.autoImport._
|
|
||||||
|
|
||||||
object ConfigBuild extends Build {
|
|
||||||
val unpublished = Seq(
|
|
||||||
// no artifacts in this project
|
|
||||||
publishArtifact := false,
|
|
||||||
// make-pom has a more specific publishArtifact setting already
|
|
||||||
// so needs specific override
|
|
||||||
publishArtifact in makePom := false,
|
|
||||||
// no docs to publish
|
|
||||||
publishArtifact in packageDoc := false,
|
|
||||||
// can't seem to get rid of ivy files except by no-op'ing the entire publish task
|
|
||||||
publish := {},
|
|
||||||
publishLocal := {}
|
|
||||||
)
|
|
||||||
|
|
||||||
object sonatype extends PublishToSonatype {
|
|
||||||
def projectUrl = "https://github.com/typesafehub/config"
|
|
||||||
def developerId = "havocp"
|
|
||||||
def developerName = "Havoc Pennington"
|
|
||||||
def developerUrl = "http://ometer.com/"
|
|
||||||
def scmUrl = "git://github.com/typesafehub/config.git"
|
|
||||||
}
|
|
||||||
|
|
||||||
override val settings = super.settings ++ Seq(isSnapshot <<= isSnapshot or version(_ endsWith "-SNAPSHOT"))
|
|
||||||
|
|
||||||
lazy val commonSettings: Seq[Setting[_]] = unpublished ++ Seq(javaVersionPrefix in javaVersionCheck := None)
|
|
||||||
|
|
||||||
lazy val rootSettings: Seq[Setting[_]] =
|
|
||||||
commonSettings ++
|
|
||||||
Seq(aggregate in doc := false,
|
|
||||||
doc := (doc in (configLib, Compile)).value,
|
|
||||||
aggregate in packageDoc := false,
|
|
||||||
packageDoc := (packageDoc in (configLib, Compile)).value,
|
|
||||||
aggregate in checkstyle := false,
|
|
||||||
checkstyle := (checkstyle in (configLib, Compile)).value)
|
|
||||||
|
|
||||||
lazy val root = Project(id = "root",
|
|
||||||
base = file("."),
|
|
||||||
settings = rootSettings) aggregate(testLib, configLib,
|
|
||||||
simpleLibScala, simpleAppScala, complexAppScala,
|
|
||||||
simpleLibJava, simpleAppJava, complexAppJava)
|
|
||||||
|
|
||||||
lazy val configLib = Project(id = "config",
|
|
||||||
base = file("config"),
|
|
||||||
settings =
|
|
||||||
sonatype.settings ++
|
|
||||||
osgiSettings ++
|
|
||||||
Seq(
|
|
||||||
OsgiKeys.exportPackage := Seq("com.typesafe.config", "com.typesafe.config.impl"),
|
|
||||||
publish := sys.error("use publishSigned instead of plain publish"),
|
|
||||||
publishLocal := sys.error("use publishLocalSigned instead of plain publishLocal")
|
|
||||||
)).enablePlugins(SbtOsgi) dependsOn testLib % "test->test"
|
|
||||||
|
|
||||||
def project(id: String, base: File) = Project(id, base, settings = commonSettings)
|
|
||||||
|
|
||||||
lazy val testLib = project("config-test-lib", file("test-lib"))
|
|
||||||
|
|
||||||
lazy val simpleLibScala = project("config-simple-lib-scala", file("examples/scala/simple-lib")) dependsOn configLib
|
|
||||||
lazy val simpleAppScala = project("config-simple-app-scala", file("examples/scala/simple-app")) dependsOn simpleLibScala
|
|
||||||
lazy val complexAppScala = project("config-complex-app-scala", file("examples/scala/complex-app")) dependsOn simpleLibScala
|
|
||||||
|
|
||||||
lazy val simpleLibJava = project("config-simple-lib-java", file("examples/java/simple-lib")) dependsOn configLib
|
|
||||||
lazy val simpleAppJava = project("config-simple-app-java", file("examples/java/simple-app")) dependsOn simpleLibJava
|
|
||||||
lazy val complexAppJava = project("config-complex-app-java", file("examples/java/complex-app")) dependsOn simpleLibJava
|
|
||||||
}
|
|
||||||
|
|
||||||
// from https://raw.github.com/paulp/scala-improving/master/project/PublishToSonatype.scala
|
|
||||||
|
|
||||||
abstract class PublishToSonatype {
|
|
||||||
val ossSnapshots = "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
|
||||||
val ossStaging = "Sonatype OSS Staging" at "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
|
||||||
|
|
||||||
def projectUrl: String
|
|
||||||
def developerId: String
|
|
||||||
def developerName: String
|
|
||||||
def developerUrl: String
|
|
||||||
|
|
||||||
def licenseName = "Apache License, Version 2.0"
|
|
||||||
def licenseUrl = "http://www.apache.org/licenses/LICENSE-2.0"
|
|
||||||
def licenseDistribution = "repo"
|
|
||||||
def scmUrl: String
|
|
||||||
def scmConnection = "scm:git:" + scmUrl
|
|
||||||
|
|
||||||
def generatePomExtra: xml.NodeSeq = {
|
|
||||||
<url>{ projectUrl }</url>
|
|
||||||
<licenses>
|
|
||||||
<license>
|
|
||||||
<name>{ licenseName }</name>
|
|
||||||
<url>{ licenseUrl }</url>
|
|
||||||
<distribution>{ licenseDistribution }</distribution>
|
|
||||||
</license>
|
|
||||||
</licenses>
|
|
||||||
<scm>
|
|
||||||
<url>{ scmUrl }</url>
|
|
||||||
<connection>{ scmConnection }</connection>
|
|
||||||
</scm>
|
|
||||||
<developers>
|
|
||||||
<developer>
|
|
||||||
<id>{ developerId }</id>
|
|
||||||
<name>{ developerName }</name>
|
|
||||||
<url>{ developerUrl }</url>
|
|
||||||
</developer>
|
|
||||||
</developers>
|
|
||||||
}
|
|
||||||
|
|
||||||
def settings: Seq[Setting[_]] = Seq(
|
|
||||||
publishMavenStyle := true,
|
|
||||||
publishTo <<= isSnapshot { (snapshot) => Some(if (snapshot) ossSnapshots else ossStaging) },
|
|
||||||
publishArtifact in Test := false,
|
|
||||||
pomIncludeRepository := (_ => false),
|
|
||||||
pomExtra := generatePomExtra
|
|
||||||
)
|
|
||||||
}
|
|
48
project/PublishToSonatype.scala
Normal file
48
project/PublishToSonatype.scala
Normal file
@ -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 = {
|
||||||
|
<url>{ projectUrl }</url>
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>{ licenseName }</name>
|
||||||
|
<url>{ licenseUrl }</url>
|
||||||
|
<distribution>{ licenseDistribution }</distribution>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
<scm>
|
||||||
|
<url>{ scmUrl }</url>
|
||||||
|
<connection>{ scmConnection }</connection>
|
||||||
|
</scm>
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>{ developerId }</id>
|
||||||
|
<name>{ developerName }</name>
|
||||||
|
<url>{ developerUrl }</url>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
}
|
||||||
|
|
||||||
|
def settings: Seq[Setting[_]] = Seq(
|
||||||
|
publishMavenStyle := true,
|
||||||
|
publishTo := Some(if (isSnapshot.value) ossSnapshots else ossStaging),
|
||||||
|
publishArtifact in Test := false,
|
||||||
|
pomIncludeRepository := (_ => false),
|
||||||
|
pomExtra := generatePomExtra
|
||||||
|
)
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
sbt.version=0.13.11
|
sbt.version=0.13.16
|
||||||
|
Loading…
Reference in New Issue
Block a user