From 442525fd8aafc08c3c77858c11096b930d5dba2d Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Tue, 4 Sep 2018 22:25:14 +0200 Subject: [PATCH] refs: #384 backport spec wording fixes and enhancements from ConfigJSR All changes originally by me, except 1 smallish fixes by Tomas Langer. --- spec/src/main/asciidoc/architecture.asciidoc | 12 ++-- .../src/main/asciidoc/configaccessor.asciidoc | 65 +++++++++++++++++ .../src/main/asciidoc/configexamples.asciidoc | 10 ++- spec/src/main/asciidoc/configsources.asciidoc | 12 +++- spec/src/main/asciidoc/converters.asciidoc | 70 +++++++++++++------ spec/src/main/asciidoc/license-alv2.asciidoc | 2 +- .../microprofile-config-spec.asciidoc | 4 +- spec/src/main/asciidoc/release_notes.asciidoc | 17 +++++ 8 files changed, 159 insertions(+), 33 deletions(-) create mode 100644 spec/src/main/asciidoc/configaccessor.asciidoc diff --git a/spec/src/main/asciidoc/architecture.asciidoc b/spec/src/main/asciidoc/architecture.asciidoc index e1a33aef..d279a71c 100644 --- a/spec/src/main/asciidoc/architecture.asciidoc +++ b/spec/src/main/asciidoc/architecture.asciidoc @@ -23,21 +23,21 @@ It also defines ways to extend the configuration mechanism itself via a SPI (Ser === Rationale -Released binaries often contain functionality which need to behave slightly differently depending on the deployment. -This might be different REST endpoints to talk to (e.g. depending on the customer for whom a WAR is deployed). +Released binaries often contain functionality which needs to behave slightly differently depending on the deployment. +This might be the port numbers and URLs of REST endpoints to talk to (e.g. depending on the customer for whom a WAR is deployed). Or it might even be whole features which need to be switched on and off depending on the installation. All this must be possible without the need to re-package the whole application binary. Microprofile-Config provides a way to achieve this goal by aggregating configuration from many different <> and presents a single merged view to the user. This allows the application to bundle default configuration within the application. -It also allows to override the defaults from outside, e.g. via an environment variable a Java system property or via Docker. +It also allows to override the defaults from outside, e.g. via an environment variable a Java system property or via a container like Docker. Microprofile-Config also allows to implement and register own configuration sources in a portable way, e.g. for reading configuration values from a shared database in an application cluster. Internally, the core Microprofile-Config mechanism is purely String/String based. -Type-safety is only provided on top of that by using the proper <> before handing the value out to the caller. +Type-safety is intentionally only provided on top of that by using the proper <> before handing the value out to the caller. -The configuration key might use dot-separated to prevent name conflicts. This is similar to Java package namespacing: +The configuration key might use dot-separated blocks to prevent name conflicts. This is similar to Java package namespacing: [source, text] ---- @@ -50,3 +50,5 @@ some.library.own.config=some value ---- +TIP: while the above example is in the java property file syntax the actual content could also e.g. be read from a database. + diff --git a/spec/src/main/asciidoc/configaccessor.asciidoc b/spec/src/main/asciidoc/configaccessor.asciidoc new file mode 100644 index 00000000..3fe8d264 --- /dev/null +++ b/spec/src/main/asciidoc/configaccessor.asciidoc @@ -0,0 +1,65 @@ +// +// Copyright (c) 2016-2018 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Contributors: +// Mark Struberg + +[[configaccessor]] +== ConfigAccessor + + +The `ConfigAccessor` API is intended for typed configuration values and precise control over resolution. + +=== ConfigAccessor Usage + +The simplest usage of the API is resolution of a String property, equivalent to a call to `Config.getValue(propertyKey, String.class)`. +This can also be handled via `ConfigAccessor` as shown in the following example: + +.Simple example of ConfigAccessor +[source,java] +----------------------------------------------------------------- +String userName = config.access("user.name").getValue(); +----------------------------------------------------------------- + +Additional to this the `ConfigAccessor` also allows for far more complex control over the access to configured values. + +The call to `config.access(..)` returns a `ConfigAccessor`. +This is basically a builder which has methods to refine the resolution, including the following: + +* `as(Class clazz)` -- defines the return type of the property +* `asList()` -- Declare the ConfigAccessor to return a List of the given Type. +* `evaluateVariables(boolean evaluateVariables)` +* `useConverter(Converter converter)` -- Defines a specific {@link Converter} to be used instead of applying the default Converter resolving logic. +* `withLookupSuffix(String postfixName)` -- sets a parameter for the resolution of a 'postfix' for the resolution +* `withLookupSuffix(ConfigAccessor)` -- you can also use a `ConfigAccessor` to determine the 'postfix' for the resolution +* `withDefault(T value)` -- sets the default value, used in case the resolution returns `null` +* `getValue()` -- returns the resolved value with the appropriate type + +.A more complete example of ConfigAccessor +[source,java] +----------------------------------------------------------------- +Config config = ConfigProvider.getConfig(); + +ConfigAccessor dbPortCfg + = config.access("db.port") + .as(Integer.class) + .addLookupSuffix(config.access("project.projectStage").cacheFor(12, TimeUnit.HOURS)) + .addLookupSuffix("database.vendor") + .withDefault(3306); +... +Integer port = dbPortCfg.getValue(); +----------------------------------------------------------------- diff --git a/spec/src/main/asciidoc/configexamples.asciidoc b/spec/src/main/asciidoc/configexamples.asciidoc index 67944fe5..fadad21d 100644 --- a/spec/src/main/asciidoc/configexamples.asciidoc +++ b/spec/src/main/asciidoc/configexamples.asciidoc @@ -20,10 +20,12 @@ // Emily Jiang [[configexamples]] -== Configuration Usage Examples +== Config Usage Examples -A configuration object can be obtained programmatically via the `ConfigProvider` or automatically via `@Inject Config`. An application can then access its configured values via a `Config` instance. +An application can obtain it's configuration programmatically via the `ConfigProvider`. +In CDI enabled beans it can also get injected via `@Inject Config`. +An application can then access its configured values via this `Config` instance. === Simple Programmatic Example @@ -42,7 +44,7 @@ public class ConfigUsageSample { } ---- -If you need to access a different server then you can e.g. change the configuration via a `-D` system property: +If you need to access a different server then you can e.g. change the configuration via a Java `-D` system property: [source, text] ---- @@ -77,12 +79,14 @@ public class InjectedConfigUsageSample { @Inject @ConfigProperty(name="myprj.some.url") private String someUrl; + //The following code injects an Optional value of myprj.some.port property. //Contrary to natively injecting the configured value this will not lead to a //DeploymentException if the configured value is missing. @Inject @ConfigProperty(name="myprj.some.port") private Optional somePort; + //Injects a Provider for the value of myprj.some.dynamic.timeout property to //resolve the property dynamically. Each invocation to Provider#get() will //resolve the latest value from underlying Config. diff --git a/spec/src/main/asciidoc/configsources.asciidoc b/spec/src/main/asciidoc/configsources.asciidoc index 5088d84d..a5612028 100644 --- a/spec/src/main/asciidoc/configsources.asciidoc +++ b/spec/src/main/asciidoc/configsources.asciidoc @@ -36,6 +36,13 @@ It can also be used to implement a drop-in configuration approach. Simply create a jar containing a `ConfigSource` with a higher ordinal and override configuration values in it. If the jar is present on the classpath then it will override configuration values from <> with lower `ordinal` values. +=== Manually defining the Ordinal of a built-in ConfigSource + +Note that a special property `config_ordinal` can be set within any built-in `ConfigSource` implementation. +The default implementation of `getOrdinal()` will attempt to read this value. +If found and a valid integer, the value will be used. +Otherwise the respective default value will be used. + [source, text] ---- config_ordinal = 120 @@ -43,8 +50,6 @@ com.acme.myproject.someserver.url = http://more_important.server/some/endpoint ---- -Note that `config_ordinal` can be set within any `ConfigSource` implementation. The default implementation of `getOrdinal()` will attempt to read this value, if found and a valid integer, the value will be used, otherwise the default of `100` will be returned. - [[default_configsources]] === Default ConfigSources @@ -142,6 +147,9 @@ public class ExampleYamlConfigSourceProvider Please note that a single `ConfigSource` should be either registered directly or via a `ConfigSourceProvider`, but never both ways. +=== Cleaning up a ConfigSource + +If a `ConfigSource` implements the `java.lang.AutoCloseable` interface then the `close()` method will be called when the underlying `Config` is being released. === ConfigSource and Mutable Data diff --git a/spec/src/main/asciidoc/converters.asciidoc b/spec/src/main/asciidoc/converters.asciidoc index a58d7849..ce0f3382 100644 --- a/spec/src/main/asciidoc/converters.asciidoc +++ b/spec/src/main/asciidoc/converters.asciidoc @@ -37,6 +37,7 @@ The following `Converter` s are provided by Microprofile-Config by default: * `float` and `Float` , a dot '.' is used to separate the fractional digits * `double` and `Double` , a dot '.' is used to separate the fractional digits * `Class` based on the result of `Class.forName` +* `URL` All built-in `Converter` have the `@Priority` of `1`. @@ -54,41 +55,68 @@ The `Config` will use the `Converter` with the highest `Priority` for each targe A custom `Converter` for a target type of any of the built-in Converters will overwrite the default Converter. Converters can be added to the `ConfigBuilder` programmatically via `ConfigBuilder#withConverters(Converter... converters)` -where the type of the converters can be obtained via reflection. However, this is not possible for a lambda converter. In this case, use the method `ConfigBuilder#withConverter(Class type, int priority, Converter converter)`. +where the type of the converters can be obtained via reflection. However, this is not possible for a lambda converter. +In this case, use the method `ConfigBuilder#withConverter(Class type, int priority, Converter converter)`. + +=== Implicit Converters + +If no built-in nor custom `Converter` for a requested Type `T`, an implicit Converter is automatically provided if the following conditions are met: + +!TODO rule option A: +* The target type {@code T} has a {@code public static T of(String)} method, or +* The target type {@code T} has a {@code public static T valueOf(String)} method, or +* The target type {@code T} has a public Constructor with a String parameter, or +* The target type {@code T} has a {@code public static T parse(CharSequence)} method + +!TODO rule option B: +* The target type `T` has a Constructor with a String parameter, or +* the target type `T` has a Constructor with a CharSequence parameter, or +* the target type `T` has a `static T valueOf(String)` method, or +* the target type `T` has a `static T valueOf(CharSequence)` method, or +* the target type `T` has a `static T parse(String)` method, or +* the target type `T` has a `static T parse(CharSequence)` method, or + +The lookup will be done in the order of the above list. + +Note that every `java.time` type has a `parse(CharSequence)` method and every enum has a generated 'valueOf(String)' method. +They are thus all covered by an implicit converter! + +If an Implicit Converter cannot convert a value, a `java.lang.IllegalArgumentException` is to be thrown. === Array Converters -For the built-in converters and custom converters, the corresponding Array converters are provided -by default. The delimiter for the config value is ",". The escape character is "\". -e.g. With this config `myPets=dog,cat,dog\\,cat`, the values as an array will be -`{"dog", "cat", "dog,cat"}`. +For the built-in converters and custom converters, the corresponding Array converters are provided by default. +The delimiter for the config value is ",". +The escape character is "\". +e.g. With this config `myPets=dog,cat,dog\,cat`, the values as an array will be `{"dog", "cat", "dog,cat"}`. -* Programmatic lookup - An array as a class is supported in the programmatic lookup. +==== Programmatic lookup +Array as a class type is supported in the programmatic lookup. +[source, java] ---- - private String[] myPets = config.getValue("myPets", String[].class); + String[] myPets = config.getValue("myPets", String[].class); ---- - myPets will be "dog", "cat", "dog,cat" as an array -* Injection model - For the property injection, Array, List and Set should be supported. +myPets will be "dog", "cat", "dog,cat" as an array + +==== Injection model +For the property injection, Array, List and Set are supported. +[source, java] ---- - @Inject @ConfigProperty(name="myPets") private String[] myArrayPets; - @Inject @ConfigProperty(name="myPets") private List myListPets; - @Inject @ConfigProperty(name="myPets") private Set mySetPets; +@Inject @ConfigProperty(name="myPets") String[] myPetsArray; +@Inject @ConfigProperty(name="myPets") List myPetsList; +@Inject @ConfigProperty(name="myPets") Set myPetsSet; ---- - myPets will be "dog", "cat", "dog,cat" as an array, List or Set. -=== Automatic Converters +myPets will be "dog", "cat", "dog,cat" as an array, List or Set. + +=== Cleaning up a Converter + +If a `Converter` implements the `java.lang.AutoCloseable` interface then the `close()` method will be called when the underlying `Config` is being released. -If no built-in nor custom `Converter` for a requested Type `T`, an implicit Converter is automatically provided if the following conditions are met: -* The target type {@code T} has a {@code public static T of(String)} method, or -* The target type {@code T} has a {@code public static T valueOf(String)} method, or -* The target type {@code T} has a public Constructor with a String parameter, or -* The target type {@code T} has a {@code public static T parse(CharSequence)} method diff --git a/spec/src/main/asciidoc/license-alv2.asciidoc b/spec/src/main/asciidoc/license-alv2.asciidoc index 8b17e6bd..3824b5b0 100644 --- a/spec/src/main/asciidoc/license-alv2.asciidoc +++ b/spec/src/main/asciidoc/license-alv2.asciidoc @@ -26,7 +26,7 @@ Status: {revremark} Release: {revdate} -Copyright (c) 2016-2017 Contributors to the Eclipse Foundation +Copyright (c) 2016-2018 Contributors to the Eclipse Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/spec/src/main/asciidoc/microprofile-config-spec.asciidoc b/spec/src/main/asciidoc/microprofile-config-spec.asciidoc index b27da67b..dba13e33 100644 --- a/spec/src/main/asciidoc/microprofile-config-spec.asciidoc +++ b/spec/src/main/asciidoc/microprofile-config-spec.asciidoc @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Eclipse Microprofile Contributors: +// Copyright (c) 2016-2018 Eclipse Microprofile Contributors: // Mark Struberg // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -46,4 +46,6 @@ include::configsources.asciidoc[] include::converters.asciidoc[] +include::configaccessor.asciidoc[] + include::release_notes.asciidoc[] diff --git a/spec/src/main/asciidoc/release_notes.asciidoc b/spec/src/main/asciidoc/release_notes.asciidoc index 48fb3326..961935e6 100644 --- a/spec/src/main/asciidoc/release_notes.asciidoc +++ b/spec/src/main/asciidoc/release_notes.asciidoc @@ -108,3 +108,20 @@ More CTS were added: Java2 security related change (link:https://github.com/eclipse/microprofile-config/issues/343[#343]) + +[[release_notes_14]] +== Release Notes for MicroProfile Config 1.4 + +The following changes occurred in the 1.4 release, compared to 1.2 + +A full list of changes may be found on the link:https://github.com/eclipse/microprofile-config/milestone/5?closed=1[MicroProfile Config 1.4 Milestone] + +=== API/SPI Changes + +MicroProfile 1.4 introduced a way to deal with dynamic values. +To support this feature from the user side we introduced the `ConfigAccessor`. +We also introduced a way to have `ConfigSources` notify the `Config` about an underlying attribute change. + +=== Functional Changes + +OSGi compatibility got improved. \ No newline at end of file