Skip to content

Commit

Permalink
Support variable expansion
Browse files Browse the repository at this point in the history
* Spec
  * Update specification with Variable Expansion chapter
  * Add a note about backwards compatibilty
* API
  * rename evaluateVariables() to expandVariables()
  * add ConfigBuilder#expandVariables
* TCK
  * Add VariableExpansionTest to the TCK to validate variable expansion

Signed-off-by: Jeff Mesnil <[email protected]>
  • Loading branch information
jmesnil committed Feb 25, 2019
1 parent d46dd14 commit ccb6d13
Show file tree
Hide file tree
Showing 10 changed files with 472 additions and 19 deletions.
4 changes: 0 additions & 4 deletions api/src/main/java/org/eclipse/microprofile/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ public interface Config {
*
* If this method gets used very often then consider to locally store the configured value.
*
* <p>Note that no variable replacement like in {@link ConfigAccessorBuilder#evaluateVariables(boolean)} will be performed!
*
* @param <T> the property type
* @param propertyName
* The configuration propertyName.
Expand All @@ -121,8 +119,6 @@ public interface Config {
*
* If this method is used very often then consider to locally store the configured value.
*
* <p>Note that no variable replacement like in {@link ConfigAccessorBuilder#evaluateVariables(boolean)} will be performed!
*
* @param <T> the property type
* @param propertyName
* The configuration propertyName.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,23 @@ public interface ConfigAccessorBuilder<T> {
ConfigAccessorBuilder<T> cacheFor(Duration duration);

/**
* Whether to evaluate variables in configured values.
* Whether to expand variables in configured values.
* A variable starts with '${' and ends with '}', e.g.
* <pre>
* mycompany.some.url=${myserver.host}/some/path
* myserver.host=http://localhost:8081
* </pre>
* If 'evaluateVariables' is enabled, the result for the above key
* If 'expandVariables' is enabled, the result for the above key
* {@code "mycompany.some.url"} would be:
* {@code "http://localhost:8081/some/path"}
*
* <p><b>ATTENTION:</b> This defaults to {@code true}! That means variable replacement is enabled by default!</p>
* <p><b>ATTENTION:</b> This defaults to {@code true}! That means variable expansion is enabled by default!</p>
*
* @param evaluateVariables whether to evaluate variables in values or not
* @param expandVariables whether to expand variables in values or not
*
* @return This builder
*/
ConfigAccessorBuilder<T> evaluateVariables(boolean evaluateVariables);
ConfigAccessorBuilder<T> expandVariables(boolean expandVariables);

/**
* Build a ConfigAccessor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@
String defaultValue() default UNCONFIGURED_VALUE;

/**
* @see org.eclipse.microprofile.config.ConfigAccessorBuilder#evaluateVariables(boolean)
* @return whether variable replacement is enabled. Defaults to {@code true}.
* @see org.eclipse.microprofile.config.ConfigAccessorBuilder#expandVariables(boolean)
* @return whether variable expansion is enabled. Defaults to {@code true}.
*/
@Nonbinding
boolean evaluateVariables() default true;
boolean expandVariables() default true;

/**
* Only valid for injection of dynamically readable values, e.g. {@code Provider<String>}!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ public interface ConfigBuilder {
*/
ConfigBuilder withConverters(Converter<?>... converters);

/**
* Determines whether variable expansion is enabled.
*
* This enables variable expansion globally for any `Config` built by this builder.
* However, it is possible to override this by using
* {@link org.eclipse.microprofile.config.ConfigAccessorBuilder#expandVariables(boolean)}.
*
* @param expandVariables {@code true} to expand variables
* @return the ConfigBuilder
*/
ConfigBuilder expandVariables(boolean expandVariables);

/**
* Add the specified {@link Converter} for the given type.
Expand Down
2 changes: 2 additions & 0 deletions spec/src/main/asciidoc/microprofile-config-spec.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ include::configprovider.asciidoc[]

include::configsources.asciidoc[]

include::variable-expansion.asciidoc[]

include::converters.asciidoc[]

include::configaccessor.asciidoc[]
Expand Down
11 changes: 7 additions & 4 deletions spec/src/main/asciidoc/release_notes.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,19 @@ Java2 security related change (link:https://github.com/eclipse/microprofile-conf
[[release_notes_14]]
== Release Notes for MicroProfile Config 1.4

The following changes occurred in the 1.4 release, compared to 1.2
The following changes occurred in the 1.4 release, compared to 1.3

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]
A full list of changes may be found on the link:https://github.com/eclipse/microprofile-config/milestone/7[MicroProfile Config 1.4 Milestone]

=== API/SPI Changes

MicroProfile 1.4 introduced a way to deal with dynamic values.
MicroProfile Config 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.

MicroProfile Config now supports variable expansion so that a Config property value can refer to other config properties.

=== Functional Changes

OSGi compatibility got improved.
OSGi compatibility got improved.
137 changes: 137 additions & 0 deletions spec/src/main/asciidoc/variable-expansion.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//
// Copyright (c) 2016-2019 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.

[[variable-expansion]]
== Variable Expansion

The value of a configuration property can contains a variable corresponding to another configuration property.
This variable is _expanded_ to be replaced by its own value when the original property is returned.

For example, let's define a `server.url` property:

[source]
----
server.url=http://${server.host}:{server.port}/endpoint
----

When the Config API returns the value of `server.url`, it expands any variable (such as `server.host` and `server.port`) to replace
them with their value.

If these properties are also defined:

[source]
----
server.host=example.org
server.port=8080
----

The `server.url` value returned by the Config API is `http://example.org:8080/endpoint`.

[NOTE]
====
Backwards Compatibility
Variable expansion is not backwards compatible.
Previous version of MicroProfile Config would not expand the variables and would return the _raw_ property `http://${server.host}:{server.port}/endpoint`.
Implementation of MicroProfile Config MUST provide a way to disable variable expansion to provide backwards compatibility.
The property `mp-config.expand-variables` can be configured as a boolean in one of the 3 default `ConfigSource` (environment variables, system properties,
`META-INF/microprofile-config.properties`). Its value will be used by the MicroProfile Config implementation to _globally_ enable or disable variable expansion.
In the absence of this property, variable expansion is enabled.
====

---


=== Variable Syntax

The syntax to use a variable is `${key}` for a _required_ config property and `${<key>:<default_value>}` for
an _optional_ config property.

If the variable is required , by using `${key}`, a config property with the name `key` MUST be present in the Config to be expanded, otherwise, the
Config API will throw a `NoSuchElementException` (or an `Optional.empty()` value).

For example, let's define the configuration properties:

[source]
----
server.url=http://${server.host}:{server.port}/endpoint
server.host=example.org
----

A call to `Config.getValue("server.url", String.class)` will throw a `NoSuchElementException` since the
`server.port` property is not configured.
Likewise, a call to `Config.getOptionalValue("server.url", String.class)` will return an `Optional.empty()`.

==== Default value

A variable can define a _default_ value to be expanded if the corresponding config property is not present.

For example, let's define the configuration properties:

[source]
----
server.url=http://${server.host:example.org}:{server.port:8080}/endpoint
----

If neither `server.host` nor `server.port` config properties are present, the Config API will use the variable's default values and will return
`http://example.org:8080/endpoint` for the `server.url`.

It is possible to define an empty default value for a variable.
If the key is not present in the Config, it will not be expanded.

For example, let's define the configuration properties:

[source]
----
server.url=http://example.org:8080/endpoint#${server.anchor:}
----

When the `server.anchor` configuration property is not present, the Config API will return
`http://example.org:8080/endpoint#` for the `server.url`.

If the `server.anchor` is configured with the value `id123`, the Config API will return
`http://example.org:8080/endpoint#id123` for the `server.url`.

A default value does not support variable expansion (a default value is _raw_).

For example, let's define the configuration properties:

[source]
----
foo.one=Hello
foo.two=${foo.three:${foo.one}}
----

`foo.two` defines a variable which key is `foo.three` and which default value is `${foo.one}`.
If the config property `foo.three` is not present, the Config API will return `${foo.one}` for the `foo.two` config property.

=== Recursive variable replacement

It is possible to have two configuration properties referencing each other with variables:

[source]
----
prop1 = ${prop2}
prop2 = ${prop1}
----

Such infinite variable expansion is not supported and the Config API will throw a `NoSuchElementException` (or
return an `Optional.empty()`) value for both properties.

Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,11 @@ public void testVariableReplacement() {
Assert.assertEquals(config.access("tck.config.variable.secondEndpoint", String.class).build().getValue(),
"http://some.host.name/endpointTwo");

// variables in Config.getValue and getOptionalValue do not get evaluated otoh
Assert.assertEquals(config.getValue("tck.config.variable.firstEndpoint", String.class),
"http://${tck.config.variable.baseHost}/endpointOne");
"http://some.host.name/endpointOne");

Assert.assertEquals(config.getOptionalValue("tck.config.variable.firstEndpoint", String.class).get(),
"http://${tck.config.variable.baseHost}/endpointOne");
"http://some.host.name/endpointOne");
}

@Test
Expand Down
Loading

0 comments on commit ccb6d13

Please sign in to comment.