From 76b7d5626a83d7ed0ee5ec23daa3e0335ee62d76 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 8 Jul 2021 17:24:47 +0200 Subject: [PATCH] SE Config Profiles (#3113) * Support for inlined config source in meta configuration Signed-off-by: Tomas Langer * Support for config profiles in SE Config Signed-off-by: Tomas Langer * Config profiles example. Signed-off-by: Tomas Langer * Config profile documentation Signed-off-by: Tomas Langer --- config/config/etc/checkstyle-suppressions.xml | 28 -- config/config/etc/spotbugs/exclude.xml | 5 + .../helidon/config/InlinedConfigSource.java | 30 ++ .../io/helidon/config/MetaConfigFinder.java | 157 ++++++++- .../java/io/helidon/config/MetaProviders.java | 4 +- config/config/src/main/java/module-info.java | 4 +- .../config/ConfigSourceMetaConfigTest.java | 26 +- docs/se/config/01_introduction.adoc | 24 +- docs/se/config/02_config-sources.adoc | 12 +- docs/se/config/03_hierarchical-features.adoc | 6 +- docs/se/config/04_property-mapping.adoc | 8 +- docs/se/config/05_mutability-support.adoc | 4 +- docs/se/config/06_advanced-configuration.adoc | 209 +----------- docs/se/config/07_extensions.adoc | 30 +- docs/se/config/08_supported-formats.adoc | 8 +- docs/se/config/09_config-profiles.adoc | 308 ++++++++++++++++++ docs/se/guides/03_config.adoc | 55 ++-- examples/config/pom.xml | 1 + examples/config/profiles/README.md | 31 ++ .../config/profiles/config-profile-prod.yaml | 22 ++ .../config/profiles/config/config-prod.yaml | 17 + examples/config/profiles/pom.xml | 65 ++++ .../examples/config/profiles/Main.java | 38 +++ .../config/profiles/package-info.java | 20 ++ .../src/main/resources/application-local.yaml | 17 + .../src/main/resources/application-stage.yaml | 17 + .../src/main/resources/application.yaml | 17 + .../main/resources/config-profile-dev.yaml | 20 ++ .../main/resources/config-profile-stage.yaml | 20 ++ 29 files changed, 900 insertions(+), 303 deletions(-) delete mode 100644 config/config/etc/checkstyle-suppressions.xml create mode 100644 config/config/src/main/java/io/helidon/config/InlinedConfigSource.java create mode 100644 docs/se/config/09_config-profiles.adoc create mode 100644 examples/config/profiles/README.md create mode 100644 examples/config/profiles/config-profile-prod.yaml create mode 100644 examples/config/profiles/config/config-prod.yaml create mode 100644 examples/config/profiles/pom.xml create mode 100644 examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/Main.java create mode 100644 examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/package-info.java create mode 100644 examples/config/profiles/src/main/resources/application-local.yaml create mode 100644 examples/config/profiles/src/main/resources/application-stage.yaml create mode 100644 examples/config/profiles/src/main/resources/application.yaml create mode 100644 examples/config/profiles/src/main/resources/config-profile-dev.yaml create mode 100644 examples/config/profiles/src/main/resources/config-profile-stage.yaml diff --git a/config/config/etc/checkstyle-suppressions.xml b/config/config/etc/checkstyle-suppressions.xml deleted file mode 100644 index d0115e4c880..00000000000 --- a/config/config/etc/checkstyle-suppressions.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - diff --git a/config/config/etc/spotbugs/exclude.xml b/config/config/etc/spotbugs/exclude.xml index c2b40fe9698..d664ecf5ef3 100644 --- a/config/config/etc/spotbugs/exclude.xml +++ b/config/config/etc/spotbugs/exclude.xml @@ -47,6 +47,11 @@ + + + + + diff --git a/config/config/src/main/java/io/helidon/config/InlinedConfigSource.java b/config/config/src/main/java/io/helidon/config/InlinedConfigSource.java new file mode 100644 index 00000000000..7a3db09663c --- /dev/null +++ b/config/config/src/main/java/io/helidon/config/InlinedConfigSource.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +package io.helidon.config; + +import java.util.Map; + +import io.helidon.config.spi.ConfigSource; + +final class InlinedConfigSource { + private InlinedConfigSource() { + } + + static ConfigSource create(Config config) { + return MapConfigSource.create(config.detach().asMap().orElse(Map.of())); + } +} diff --git a/config/config/src/main/java/io/helidon/config/MetaConfigFinder.java b/config/config/src/main/java/io/helidon/config/MetaConfigFinder.java index fa5adaa7aec..e3bb431f687 100644 --- a/config/config/src/main/java/io/helidon/config/MetaConfigFinder.java +++ b/config/config/src/main/java/io/helidon/config/MetaConfigFinder.java @@ -28,6 +28,8 @@ import java.util.logging.Logger; import io.helidon.common.media.type.MediaTypes; +import io.helidon.config.spi.ConfigNode.ListNode; +import io.helidon.config.spi.ConfigNode.ObjectNode; import io.helidon.config.spi.ConfigSource; /** @@ -36,8 +38,36 @@ final class MetaConfigFinder { /** * System property used to set a file with meta configuration. + * This can also be used in combination with {@link #CONFIG_PROFILE_SYSTEM_PROPERTY} + * to define a custom location of profile specific files. */ public static final String META_CONFIG_SYSTEM_PROPERTY = "io.helidon.config.meta-config"; + /** + * System property used to set a configuration profile. This profile is then used to discover + * meta configuration named {@code config-profile-${config.profile}.xxx"}. + * + * @see #CONFIG_PROFILE_ENVIRONMENT_VARIABLE + */ + public static final String CONFIG_PROFILE_SYSTEM_PROPERTY = "config.profile"; + + /** + * System property used to set a configuration profile. This profile is then used to discover + * meta configuration named {@code config-profile-${config.profile}.xxx"}. + * This property is Helidon specific (in case the {@link #CONFIG_PROFILE_SYSTEM_PROPERTY} is + * in use by another component). + * + * @see #CONFIG_PROFILE_ENVIRONMENT_VARIABLE + * @see #CONFIG_PROFILE_SYSTEM_PROPERTY + */ + public static final String HELIDON_CONFIG_PROFILE_SYSTEM_PROPERTY = "helidon.config.profile"; + + /** + * Environment variable used to set a configuration profile. + * Environment variable is the most significant. + * + * @see #CONFIG_PROFILE_SYSTEM_PROPERTY + */ + public static final String CONFIG_PROFILE_ENVIRONMENT_VARIABLE = "HELIDON_CONFIG_PROFILE"; private static final Logger LOGGER = Logger.getLogger(MetaConfigFinder.class.getName()); private static final List CONFIG_SUFFIXES = List.of("yaml", "conf", "json", "properties"); @@ -67,23 +97,138 @@ private static Optional findMetaConfigSource(Function source; // check if meta configuration is configured using system property - String property = System.getProperty(META_CONFIG_SYSTEM_PROPERTY); - if (null != property) { + String metaConfigFile = System.getProperty(META_CONFIG_SYSTEM_PROPERTY); + // check name of the profile + String profileName = System.getenv(CONFIG_PROFILE_ENVIRONMENT_VARIABLE); + if (profileName == null) { + profileName = System.getProperty(HELIDON_CONFIG_PROFILE_SYSTEM_PROPERTY); + } + if (profileName == null) { + profileName = System.getProperty(CONFIG_PROFILE_SYSTEM_PROPERTY); + } + + if (metaConfigFile != null && profileName != null) { + // we have both profile name and meta configuration file defined + // this means we want to have a custom profile file (maybe a custom location) combined with a profile + int lastDot = metaConfigFile.lastIndexOf('.'); + String metaWithProfile; + if (lastDot == 0) { + // .configuration -> dev.configuration + metaWithProfile = profileName + metaConfigFile.substring(lastDot); + } else if (lastDot > 0) { + // config/profile/profile.yaml -> config/profile/profile-dev.yaml + metaWithProfile = metaConfigFile.substring(0, lastDot) + "-" + profileName + metaConfigFile.substring(lastDot); + } else { + // config/configuration -> config/configuration-dev + metaWithProfile = metaConfigFile + "-" + profileName; + } + source = findFile(metaWithProfile, "config profile"); + if (source.isPresent()) { + return source; + } + source = findClasspath(cl, metaWithProfile, "config profile"); + if (source.isPresent()) { + return source; + } + LOGGER.info("Custom profile file not found: " + metaWithProfile); + } + if (metaConfigFile == null) { + if (profileName != null) { + return Optional.of(profileSource(supportedMediaType, cl, profileName, supportedSuffixes)); + } + } else { // is it a file - source = findFile(property, "meta configuration"); + source = findFile(metaConfigFile, "meta configuration"); if (source.isPresent()) { return source; } // so it is a classpath resource? - source = findClasspath(cl, property, "meta configuration"); + source = findClasspath(cl, metaConfigFile, "meta configuration"); if (source.isPresent()) { return source; } - LOGGER.info("Meta configuration file not found: " + property); + LOGGER.info("Meta configuration file not found: " + metaConfigFile); } - return findSource(supportedMediaType, cl, META_CONFIG_PREFIX, "meta configuration", supportedSuffixes); + return findSource(supportedMediaType, cl, META_CONFIG_PREFIX, "meta configuration", supportedSuffixes) + .or(() -> findSource(supportedMediaType, cl, "config-profile.", "config profile", supportedSuffixes)); + } + + private static ConfigSource profileSource(Function supportedMediaType, + ClassLoader cl, + String profileName, + List supportedSuffixes) { + // first try to find the profile itself + // default name is `config-profile.xxx`, we start with `config-profile-${profile}.xxx` + String profileFileName = "config-profile-" + profileName + "."; + // first find files for each supported suffix (first one wins) + for (String supportedSuffix : supportedSuffixes) { + Optional profile = findFile(profileFileName + supportedSuffix, "config profile"); + if (profile.isPresent()) { + return profile.get(); + } + } + // now let's do the same thing with classpath + for (String supportedSuffix : supportedSuffixes) { + Optional profile = findClasspath(cl, profileFileName + supportedSuffix, "config profile"); + if (profile.isPresent()) { + return profile.get(); + } + } + // we did not find a config profile, let's create one with the usual suspects + // to make things easier, let's try both application.xxx and META-INF/microprofile-config.properties + ListNode.Builder sourceListBuilder = ListNode.builder(); + + sourceListBuilder.addObject(ObjectNode.builder().addValue("type", "environment-variables").build()) + .addObject(ObjectNode.builder().addValue("type", "system-properties").build()); + + // all profile files + for (String supportedSuffix : supportedSuffixes) { + addFile(sourceListBuilder, "application-" + profileName, supportedSuffix); + } + + // all profile classpath + for (String supportedSuffix : supportedSuffixes) { + addClasspath(sourceListBuilder, "application-" + profileName, supportedSuffix); + } + + // all main files + for (String supportedSuffix : supportedSuffixes) { + addFile(sourceListBuilder, "application", supportedSuffix); + } + + // all main classpath + for (String supportedSuffix : supportedSuffixes) { + addClasspath(sourceListBuilder, "application", supportedSuffix); + } + + addClasspath(sourceListBuilder, "META-INF/microprofile-config-" + profileName, "properties"); + addClasspath(sourceListBuilder, "META-INF/microprofile-config", "properties"); + + return ConfigSources.create(ObjectNode.builder() + .addList("sources", sourceListBuilder.build()) + .build()); + } + + private static void addClasspath(ListNode.Builder sourceListBuilder, String fileName, String supportedSuffix) { + sourceListBuilder.addObject(ObjectNode.builder() + .addValue("type", "classpath") + .addObject("properties", ObjectNode.builder() + .addValue("resource", fileName + "." + supportedSuffix) + .addValue("optional", "true") + .build()) + .build()); + } + + private static void addFile(ListNode.Builder sourceListBuilder, String fileName, String supportedSuffix) { + sourceListBuilder.addObject(ObjectNode.builder() + .addValue("type", "file") + .addObject("properties", ObjectNode.builder() + .addValue("path", fileName + "." + supportedSuffix) + .addValue("optional", "true") + .build()) + .build()); } private static Optional findSource(Function supportedMediaType, diff --git a/config/config/src/main/java/io/helidon/config/MetaProviders.java b/config/config/src/main/java/io/helidon/config/MetaProviders.java index 52ca04c9033..443a2a511fb 100644 --- a/config/config/src/main/java/io/helidon/config/MetaProviders.java +++ b/config/config/src/main/java/io/helidon/config/MetaProviders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. + * Copyright (c) 2019, 2021 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -265,6 +265,7 @@ private static final class BuiltInConfigSourcesProvider implements ConfigSourceP private static final String DIRECTORY_TYPE = "directory"; private static final String URL_TYPE = "url"; private static final String PREFIXED_TYPE = "prefixed"; + private static final String INLINED_TYPE = "inlined"; private static final Map> BUILT_INS = new HashMap<>(); @@ -277,6 +278,7 @@ private static final class BuiltInConfigSourcesProvider implements ConfigSourceP BUILT_INS.put(DIRECTORY_TYPE, DirectoryConfigSource::create); BUILT_INS.put(URL_TYPE, UrlConfigSource::create); BUILT_INS.put(PREFIXED_TYPE, PrefixedConfigSource::create); + BUILT_INS.put(INLINED_TYPE, InlinedConfigSource::create); } @Override diff --git a/config/config/src/main/java/module-info.java b/config/config/src/main/java/module-info.java index c80d301ca72..5d5d73c1645 100644 --- a/config/config/src/main/java/module-info.java +++ b/config/config/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. + * Copyright (c) 2017, 2021 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ /** * Helidon SE Config module. + * + * @see io.helidon.config */ module io.helidon.config { diff --git a/config/config/src/test/java/io/helidon/config/ConfigSourceMetaConfigTest.java b/config/config/src/test/java/io/helidon/config/ConfigSourceMetaConfigTest.java index f7baf641367..e31d05028ff 100644 --- a/config/config/src/test/java/io/helidon/config/ConfigSourceMetaConfigTest.java +++ b/config/config/src/test/java/io/helidon/config/ConfigSourceMetaConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. + * Copyright (c) 2020, 2021 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -211,6 +211,30 @@ public void testPrefixed() { assertThat(config.get("this.is.prefix.key.app.page-size").asInt().get(), is(10)); } + @Test + public void testInlined() { + Config metaConfig = builderFrom(ConfigSources.create( + ObjectNode.builder() + .addValue("type", "inlined") + .addObject("properties", ObjectNode.builder() + .addValue("key", "inlined-value") + .addObject("server", ObjectNode.builder() + .addValue("port", "8014") + .addValue("host", "localhost") + .build()) + .build()) + .build())) + .build(); + + ConfigSource source = singleSource(metaConfig); + + Config config = justFrom(source); + + assertThat(config.get("key").asString().get(), is("inlined-value")); + assertThat(config.get("server.port").asInt().get(), is(8014)); + assertThat(config.get("server.host").asString().get(), is("localhost")); + } + private ConfigSource singleSource(Config metaConfig) { List sources = metaConfig.as(MetaConfig::configSource).get(); diff --git a/docs/se/config/01_introduction.adoc b/docs/se/config/01_introduction.adoc index f052c52352c..aa497d72588 100644 --- a/docs/se/config/01_introduction.adoc +++ b/docs/se/config/01_introduction.adoc @@ -309,8 +309,8 @@ filters, overrides, mappers, whether or not environment variables and Java system properties serve as config sources. The JavaDoc explains how to use the link:{javadoc-base-url-api}/Config.Builder.html[`Config.Builder`]. + -or -* creating a <> +* using a <> to choose the sources to be used +* creating a <> file on the runtime classpath or file system to control how the config system prepares the default configuration. @@ -325,26 +325,26 @@ other config topics. |=== | Topic |Documentation -| Where config comes from |<>, -<> -| What format config data is expressed in |<>, -<> -| How to filter, override, and dereference values |<> -| What happens when config data changes |<> -| How to deal with loading errors |<> +| Where config comes from |<>,<>, +<> +| What format config data is expressed in |<>, +<> +| How to filter, override, and dereference values |<> +| What happens when config data changes |<> +| How to deal with loading errors |<> |=== .Accessing Configuration Data |=== | Topic |Documentation -| How config data is translated into Java types |<> -| How to navigate config trees |<> +| How config data is translated into Java types |<> +| How to navigate config trees |<> |=== .Extending and Fine-tuning the Config System |=== | Topic |Documentation -| Writing extensions |<> +| Writing extensions |<> |=== diff --git a/docs/se/config/02_config-sources.adoc b/docs/se/config/02_config-sources.adoc index ef8aa9d374d..c3c391766f8 100644 --- a/docs/se/config/02_config-sources.adoc +++ b/docs/se/config/02_config-sources.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2018, 2020 Oracle and/or its affiliates. + Copyright (c) 2018, 2021 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ When your application prepares a `Config.Builder` it sets what ``ConfigSource``s == Config Sources If your application uses the default configuration, then the config system automatically sets up the config sources for you, as described in the -<>. +<>. If instead your application uses a link:{javadoc-base-url-api}/Config.Builder.html[`Config.Builder`], then it can invoke one of the `sources` methods on that builder to set which config sources it should use. @@ -60,15 +60,15 @@ class for a complete list of the built-in config source types and how to use the You can also extend the config system to handle other types of sources by implementing the link:{javadoc-base-url-api}/spi/ConfigSource.html[`ConfigSource`] interface. See -the <> documentation for complete information. +the <> documentation for complete information. -See the <> page for further +See the <> page for further information on some more involved aspects of config sources. == Config Parsers [[parsers]] When it reads configuration text from sources, the config system uses config parsers to translate that text into the in-memory data structures representing that configuration. -The config system includes several built-in parsers, such as for the Java properties, YAML, JSON, and HOCON formats. See <> for how to change your `pom.xml` to make parsers for those formats available to your application. Then your application can invoke the @@ -82,7 +82,7 @@ Each `Config` object which the config system returns to your application is immutable; even if the information in one of the underlying config sources changes, an in-memory data structure built from the earlier content remains unchanged. -Even so, the config system allows your application to learn when such underlying changes in the data occur and respond accordingly. The <> section explains this in detail, and the link:{javadoc-base-url-api}/PollingStrategies.html[`PollingStrategies`] JavaDoc describes the built-in implementations. You can, of course, write your own by implementing the link:{javadoc-base-url-api}/spi/PollingStrategy.html[`PollingStrategy`] interface. On a config source builder invoke `pollingStrategy` with an instance of your custom strategy and then invoke `build` to create the `ConfigSource`. +Even so, the config system allows your application to learn when such underlying changes in the data occur and respond accordingly. The <> section explains this in detail, and the link:{javadoc-base-url-api}/PollingStrategies.html[`PollingStrategies`] JavaDoc describes the built-in implementations. You can, of course, write your own by implementing the link:{javadoc-base-url-api}/spi/PollingStrategy.html[`PollingStrategy`] interface. On a config source builder invoke `pollingStrategy` with an instance of your custom strategy and then invoke `build` to create the `ConfigSource`. == Dealing with Loading Errors: Retry Policies [[retry]] Config sources, especially those that depend on fallible mechanisms such as the network or a shared file system, might fail to load during momentary outages. The config system allows you to build resiliency into your application's use of configuration that relies on such technologies. diff --git a/docs/se/config/03_hierarchical-features.adoc b/docs/se/config/03_hierarchical-features.adoc index 72a2567c2b5..bf523c497b5 100644 --- a/docs/se/config/03_hierarchical-features.adoc +++ b/docs/se/config/03_hierarchical-features.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2018, 2020 Oracle and/or its affiliates. + Copyright (c) 2018, 2021 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -70,7 +70,7 @@ and `VALUE`. The following example is in link:https://github.com/lightbend/config/blob/master/HOCON.md[HOCON] (human-optimized config object notation) format. The config system supports HOCON as an -<>. +<>. [source,hocon] .HOCON `application.conf` file @@ -292,7 +292,7 @@ Sometimes it can be convenient to write part of your application to deal with configuration without it knowing if or where the relevant configuration is plugged into a larger config tree. -For example, the <> +For example, the <> from the introduction section contains several settings prefixed with `web` such as `web.page-size`. Perhaps in another config source the same information might be stored as `server.web.page-size`: diff --git a/docs/se/config/04_property-mapping.adoc b/docs/se/config/04_property-mapping.adoc index 21f73a567cf..682291e5670 100644 --- a/docs/se/config/04_property-mapping.adoc +++ b/docs/se/config/04_property-mapping.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2018, 2020 Oracle and/or its affiliates. + Copyright (c) 2018, 2021 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -98,7 +98,7 @@ which maps the current node to a type. The next example, and later ones below showing complex type mapping, use the example -<> configuration +<> configuration from the config introduction. Part of that example includes this line: [source] ---- @@ -111,7 +111,7 @@ BigDecimal initialId = config.get("bl.initial-id").as(BigDecimal.class); == Converting Configuration to Complex Types -The <> section describes +The <> section describes the tree structure used to represent config data. The config system can map subtrees of a config tree to complex Java types. @@ -184,7 +184,7 @@ changing only the _registration_ of the mapper, not each use of it. [[WebConfig]] ==== Continuing the `Web` Example The following examples build on the example configuration from the -<> +<> example file in the introduction. diff --git a/docs/se/config/05_mutability-support.adoc b/docs/se/config/05_mutability-support.adoc index 64f7ab66757..7c8c8a4952e 100644 --- a/docs/se/config/05_mutability-support.adoc +++ b/docs/se/config/05_mutability-support.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2018, 2020 Oracle and/or its affiliates. + Copyright (c) 2018, 2021 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -99,7 +99,7 @@ that source. This is called _polling_ in the Helidon API but specific change det algorithms might not use actual polling. You choose a specific link:{javadoc-base-url-api}/spi/PollingStrategy.html[`PollingStrategy`] for each config source you want to monitor. See the section on -<> in the +<> in the config extensions doc page for more information. The config system provides some built-in polling strategies, exposed as these methods diff --git a/docs/se/config/06_advanced-configuration.adoc b/docs/se/config/06_advanced-configuration.adoc index 075cff2b418..36df6b99e1d 100644 --- a/docs/se/config/06_advanced-configuration.adoc +++ b/docs/se/config/06_advanced-configuration.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2018, 2020 Oracle and/or its affiliates. + Copyright (c) 2018, 2021 Oracle and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -116,7 +116,7 @@ assert secrets.get("password") // <4> <4> ...and the key `password` to `^ery$ecretP&ssword`. Remember that your application can process the contents of a given file -as configuration. See the <> section +as configuration. See the <> section and the link:{javadoc-base-url-api}/ConfigSources.html#file-java.lang.String-[`ConfigSources.file`] JavaDoc. @@ -466,6 +466,11 @@ As before, the config system replaces the value node in the containing config tree with the config tree resulting from the additional parse. === Loading Config using Meta-configuration [[Config-Advanced-metaconfig]] + +This section is now available in <>, + which make meta-configuration obsolete - you can achieve the same with less configuration. +A profile file is a meta-configuration file selected by the rules defined in the link above. + Instead of including code in your application to construct config trees from builders, sources, etc., you can instead prepare _meta-configuration_ in a file that declares the sources to load and their attributes. @@ -486,7 +491,7 @@ before using it to construct a `Config` tree. The config system interprets the m as directions for how to build a config tree, rather than as the config data itself. ==== Loading Config from an Implicit Meta-configuration File [[Config-Advanced-Config-MetaConfig]] -The <> section shows how to use +The <> section shows how to use `Config.create()` to load config from one of several possible default config files. That same method also searches for one of several possible default meta-config files from which to load config sources to be used for the default config. @@ -510,150 +515,13 @@ first on the file system in current directory, then on classpath, checked in thi ... `application.properties` - configuration file in Java Properties format Remember that the config system will check for these default meta-config and config files -only if the classpath includes the corresponding parsers. The introduction section on <> +only if the classpath includes the corresponding parsers. The introduction section on <> section describes this further. -==== Meta-configuration File Format See javadoc link:{javadoc-base-url-api}/Config.Builder.html#config-io.helidon.config.Config-[`Config.Builder.config(Config)`]. -Meta configuration can be used to configure various options that are available on configuration -builder. -It must contain at least the list of sources to use to load configuration from. - -The root `sources` property contains an array (ordered) of objects defining each config source to -be used. -Each element of the array must contain at least the `type` property, determining the -config source type (such as `system-properties`, `file`). It may also contain a `properties` -property with additional configuration of the config source. - -===== Built-in Types [[MetaConfig-built-in-types]] -The config system supports these built-in types: - -.Built-in Meta-configuration Types -|=== -|Type |Use |Related `ConfigSources` Method |Required Properties - -|`system-properties` |System properties are a config source |`ConfigSources.systemProperties()` | n/a -|`environment-variables` |Environment variables are a config source |`ConfigSources.environmentVariables()` | n/a -|`classpath` |Specified resource is used as a config source |`ConfigSources.classpath(String)` | `resource` - path to the resource to load -|`file` |Specified file is used as a config source |`ConfigSources.file(Path)` |`path` - path to the file to load -|`directory` |Each file in directory used as config entry, with key = file name and value = file contents |`ConfigSources.directory(String)` |`path` - path to the directory to use -|`url` |Specified URL is read as a config source |`ConfigSources.url(URL)` | `url` - URL from which to load the config -|`prefixed` |Associated config source is loaded with the specified prefix |`ConfigSources.prefixed(String,Supplier)` a|* `key` - key of config element in associated source to load -* `type` - associated config source specification -* `properties` - as needed to further qualify the associated config source -|=== - -Except for the `system-properties` and `environment-variables` types, the meta-config -`properties` section for a source can also specify any optional settings for the -corresponding config source type. The JavaDoc for the related config source -type builders lists the supported properties for each type. (For example, -link:{javadoc-base-url-api}/internal/FileConfigSource.FileBuilder.html[`FileConfigSource.FileBuilder`].) - -Here is example meta-configuration in YAML format. Note how the `properties` sections -are at the same level as the `type` or `class` within a `sources` array entry. - -[source,yaml] -.Meta-configuration `meta-config.yaml` illustrating all built-in sources available on the classpath ----- -caching.enabled: false -sources: - - type: "system-properties" - - type: "environment-variables" - - type: "directory" - properties: - path: "conf/secrets" - media-type-mapping: - yaml: "application/x-yaml" - password: "application/base64" - polling-strategy: - type: "regular" - properties: - interval: "PT15S" - - type: "url" - properties: - url: "http://config-service/my-config" - media-type: "application/hocon" - optional: true - retry-policy: - type: "repeat" - properties: - retries: 3 - - type: "file" - properties: - optional: true - path: "conf/env.yaml" - polling-strategy: - type: "watch" - - type: "prefixed" - properties: - key: "app" - type: "classpath" - properties: - resource: "app.conf" - - type: "classpath" - properties: - resource: "application.conf" ----- - -Note that the example shows how your meta-configuration can configure optional features such as polling -strategies and retry policies for config sources. - -==== Meta-config for Custom Source Types -You can use meta-config to set up custom config source types as well as the -built-in ones described above. - -A custom config source can easily support meta configuration by implementing the -`io.helidon.config.spi.ConfigSourceProvider` interface and registering it as a Java service loader service. -Note that the provider can be used for multiple ConfigSource implementations. - - -Implement the `ConfigSourceProvider` -[source,java] ----- -public class MyConfigSourceProvider implements ConfigSourceProvider { - private static final String TYPE = "my-type"; - - @Override - public boolean supports(String type) { - return TYPE.equals(type); - } - - @Override - public ConfigSource create(String type, Config metaConfig) { - // as we only support one in this implementation, we can just return it - return MyConfigSource.create(metaConfig); - } - - @Override - public Set supported() { - return Collections.singleton(TYPE); - } -} ----- - -Register it as a java service loader service -[source] -.File `META-INF/services/io.helidon.config.spi.ConfigSourceProvider` ----- -io.helidon.examples.MyConfigSourceProvider ----- - -Now you can use the following meta configuration: -[source,yaml] ----- -sources: - - type: "system-properties" - - type: "environment-variables" - - type: "my-type" - properties: - my-property: "some-value" ----- - -Note that it is the `AbstractSource` SPI class that provides support for -polling strategies and retry policies. If you create custom config sources that -should also offer this support be -sure they extend `AbstractSource` or one of its subclasses to inherit this behavior. +==== Meta-configuration File Format +This section is now available in <> ==== Loading Config using Meta-configuration Here is how your application can use meta-configuration in a particular resource on the @@ -669,57 +537,8 @@ Config config = MetaConfig.config(metaConfig); // <2> <2> This method populates the config sources from all the actual sources declared in the meta-configuration. -==== Meta-config for Polling Strategies and Retry Policies - -Your meta-config can include the set-up for polling strategies and retry -policies if the config source supports them. Declare them in a way similar to -how you declare the config sources themselves: by `type` and with -accompanying `properties`. - -.Meta-config Support for Built-in Polling Strategies -|=== -|Strategy Type |Usage |Properties - -|`regular` -| Periodic polling - See link:{javadoc-base-url-api}/PollingStrategies.html#regular-java.time.Duration-[`PollingStrategies.regular`] method -|`interval` (`Duration`) - indicating how often to poll; e.g., `PT15S` represents 15 seconds - -|`watch` -| Filesystem monitoring - See link:{javadoc-base-url-api}/PollingStrategies.html#watch-java.nio.file.Path-[`PollingStrategies.watch`] method -| `path` - file system path to the `classpath`, `file`, or `directory` to monitor - -|=== - -.Meta-config Support for Built-in Retry Policies -|=== -|Policy Type |Usage |Properties - -|`repeat` -|Regularly-scheduled - see link:{javadoc-base-url-api}RetryPolicies.html#repeat-int-[`RetryPolicies.repeat`]. -a|`retries` (`int`) - number of retries to perform + - -Optional: - -* `delay` (`Duration`) - initial delay between retries -* `delay-factor` (`double`) - `delay` is repeatedly multiplied by this each retry to compute -the delay for each successive retry -* `call-timeout` (`Duration`) - timeout for a single invocation to load the source -* `overall-timeout` (`Duration`) - total timeout for all retry calls and delays -|=== - -To specify a custom polling strategy or custom retry policy, implement the interface -and then implement the `io.helidon.config.spi.PollingStrategyProvider` or -`io.helidon.config.spi.RetryPolicyProvider` to enable your custom implementations for -meta configuration. -You can then use any custom properties - these are provided as a `Config` instance to -the `create` method of the Provider implementation. - -See link:{javadoc-base-url-api}/spi/RetryPolicy.html[`RetryPolicy`] and -link:{javadoc-base-url-api}/spi/PollingStrategy.html[`PollingStrategy`] JavaDoc -sections. - == Configuration Key -As described in the <> each config node (except the root) has a non-null key. Here is the formal definition of what keys can be: [source,abnf] @@ -795,8 +614,8 @@ before returning the value, according to _filters_, _overrides_, and _tokens_. The config system provides some built-in instances of these you can use, and you can add your own as described in the sections which describe -<> and -<>. +<> and +<>. Your application can add filters and overrides explicitly to a config builder and the config system by default uses the Java service loader mechanism to diff --git a/docs/se/config/07_extensions.adoc b/docs/se/config/07_extensions.adoc index 0d60d6f6188..f52bbdbaf22 100644 --- a/docs/se/config/07_extensions.adoc +++ b/docs/se/config/07_extensions.adoc @@ -25,7 +25,7 @@ Developer-provided extensions influence how the config system behaves. -The <> explains the design of the config +The <> explains the design of the config system and how its parts work together to read and parse config data, convert it to Java types, fine-tune the look-up of config data, and reload and reprocess data when it changes. _Config extensions_ provided by the application @@ -61,17 +61,17 @@ Each config extension implements one of the interfaces defined in the Configurat The extension mechanism of Config can also use Java `ServiceLoader`. For this purpose, you implement providers that serve as factories for your implementation of an extension. -This is to support meta-configuration even for custom extensions. +This is to support config profiles even for custom extensions. Service providers: * `ConfigMapperProvider` - support for config mappers, automatically discovered by the config system * `ConfigFilter` - support for config filters, automatically discovered by the config system * `ConfigParser` - support for config parsers, automatically discovered by the config system -* `ConfigSourceProvider` - support for named config sources, configurable through meta configuration -* `ChangeWatcherProvider` - support for named change watchers, configurable through meta configuration -* `OverrideSourceProvider` - support for named override sources, configurable through meta configuration -* `PollingStrategyProvider` - support for named polling strategies, configurable through meta configuration -* `RetryPolicyProvider` - support for retry policies, configurable through meta configuration +* `ConfigSourceProvider` - support for named config sources, configurable through profiles +* `ChangeWatcherProvider` - support for named change watchers, configurable through profiles +* `OverrideSourceProvider` - support for named override sources, configurable through profiles +* `PollingStrategyProvider` - support for named polling strategies, configurable through profiles +* `RetryPolicyProvider` - support for retry policies, configurable through profiles The config system itself implements several of these SPIs, as noted in the sections below. @@ -107,8 +107,8 @@ The following extensions are loaded using a service loader for any configuration * `ConfigParser` - each config parser on the classpath that implements `ConfigParserProvider` as a Java service loader service * `ConfigFilter` - each filter on the classpath that implements `ConfigFilter` as a Java service loader service -Other extensions are only used from Java service loader when you use meta configuration. -Mapping is done through the type configured in meta configuration, and the type defined by the extension provider interface. +Other extensions are only used from Java service loader when you use config profiles. +Mapping is done through the type configured in config profile, and the type defined by the extension provider interface. For example for config sources, the interface defines the following methods (only subset shown): [source,java] @@ -117,7 +117,7 @@ boolean supports(String type); ConfigSource create(String type, Config metaConfig); ---- -Considering the following meta configuration: +Considering the following meta configuration (or config profile): [source,yaml] ---- @@ -128,7 +128,7 @@ sources: ---- The config system would iterate through all `ConfigSourceProvider` implementations found through Java `ServiceLoader` based on their priority. -First provider that returns `true` when `supports("my-type")` is called would be used, and an instance of a `ConfigSource` created using `create("my-type", config)`, where `config` is located on the node of `properties` from meta configuration. +First provider that returns `true` when `supports("my-type")` is called would be used, and an instance of a `ConfigSource` created using `create("my-type", config)`, where `config` is located on the node of `properties` from config profile. === About Priority [[priority-info]] @@ -335,7 +335,7 @@ method. The config system will apply the overrides returned from each `OverrideSource` to each config key requested from a `Config` that is based on that `Config.Builder`. -To support custom override sources in meta configuration, also implement the +To support custom override sources in config profiles, also implement the link:{javadoc-base-url-api}/spi/OverrideSourceProvider.html[`OverrideSourceProvider`] service loader SPI [plantuml,config/spi-OverrideSource,png,title="OverrideSource SPI",align="center"] @@ -519,7 +519,7 @@ interface PollingStrategy.Polled { } ---- -To support polling strategies that can be configured in meta configuration, also implement the `PollingStrategyProvider` Java service loader SPI. +To support polling strategies that can be configured in config profile, also implement the `PollingStrategyProvider` Java service loader SPI. === ChangeWatcher SPI @@ -552,7 +552,7 @@ interface ChangeWatcher.ChangeEvvent { } ---- -To support change watchers that can be configured in meta configuration, also implement the `ChangeWatcherProvider` Java service loader SPI. +To support change watchers that can be configured in config profile, also implement the `ChangeWatcherProvider` Java service loader SPI. == RetryPolicy SPI [[Config-SPI-RetryPolicy]] @@ -599,6 +599,6 @@ The application can try to cancel the overall execution of a `RetryPolicy` by in Ideally the retry policy implementation should be able to abort the execution of the retry policy, even while a function call is in progress, but the policy must respond to cancels between function calls. In either case `cancel` returns `true` if the retry was aborted without a successful call to the function, and `false` otherwise, including if the function call had already completed successfully or had previously been successfully canceled. -To support retry policies in meta configuration, also implement the Java service loader SPI +To support retry policies in config profiles, also implement the Java service loader SPI `RetryPolicyProvider`. diff --git a/docs/se/config/08_supported-formats.adoc b/docs/se/config/08_supported-formats.adoc index 9a8d612b849..dacb40c423a 100644 --- a/docs/se/config/08_supported-formats.adoc +++ b/docs/se/config/08_supported-formats.adoc @@ -296,7 +296,7 @@ Config config = Config.create( ==== Loading Meta-configuration via Etcd The config system can load information about config sources from -<> +<> rather than requiring your application to construct the builder. To read meta-configuration from an Etcd source set the following required properties for the source: @@ -402,10 +402,10 @@ Config config = Config.create( ---- <1> Use `PollingStrategies.regular(Duration duration)` to monitor for config changes. -You can also implemention your own polling strategy by implementing +You can also implement your own polling strategy by implementing link:{javadoc-base-url-api}/spi/PollingStrategy.html[`PollingStrategy`]. See -the <> and -<> discussions. +the <> and +<> discussions. ==== Loading Meta-configuration via git diff --git a/docs/se/config/09_config-profiles.adoc b/docs/se/config/09_config-profiles.adoc new file mode 100644 index 00000000000..20ee76d84de --- /dev/null +++ b/docs/se/config/09_config-profiles.adoc @@ -0,0 +1,308 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2021 Oracle and/or its affiliates. + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +:javadoc-base-url-api: {javadoc-base-url}io.helidon.config/io/helidon/config + += Configuration Profiles +:h1Prefix: SE +:description: Helidon config profiles +:keywords: helidon, config, profile +:toc: preamble +:toclevels: 4 + +Configuration profiles provide a capability to prepare structure of configuration for each +environment in advance, and then simply switch between these structures using a system property +or an environment variable. + +== Profile options +To choose a configuration profile to use at runtime, you can use: + +1. A system property `config.profile` +2. An environment variable `HELIDON_CONFIG_PROFILE` + +There are two ways to define a profile configuration: + +1. Use a config source with a <<#Profile-Source,profile specific name>> +2. Use a <<#Profile-File,profile file>> defining all configuration sources + +Configuration profiles can only be used when config is created using the `Config.create()` method without parameters. If you explicitly configure sources, profiles are ignored. + +== Profile Config Sources [[Profile-Source]] + +If a profile is specified, config will load the profile-specific default configuration source +before the "main" source. + +Let's consider the selected profile is `dev`, and we have `yaml` configuration support on classpath; +config will look for the following sources (in this order): + +1. `application-dev.yaml` on file system +2. `application-dev.properties` on file system +3. `application-dev.yaml` on classpath +4. `application-dev.properties` on classpath +5. `application.yaml` on file system +6. `application.properties` on file system +7. `application.yaml` on classpath +8. `application.properties` on classpath + +== Profile Files [[Profile-File]] + +If a profile is specified, config will look for a profile-specific "meta configuration". + +Let's consider the selected profile is `dev`, and we have `yaml` configuration support on classpath; +config will look for the following profiles (in this order): + +1. `config-profile-dev.yaml` on file system +2. `config-profile-dev.properties` on file system +3. `config-profile-dev.yaml` on classpath +4. `config-profile-dev.properties` on classpath + +If any of these files is discovered, it would be used to set up the configuration. In case none is found, + the config falls back to <<#Profile Config Sources,profile specific config sources>>. + +The structure of the file is described below in <<#Profile File Format,profile file format>>. + +In case you need to customize the location of the profile file, you can use the system property +`io.helidon.config.meta-config`. For example if it is configured to `config/profile.yaml`, +config looks for file `config/profile-dev.yaml` when `dev` profile is configured. + +=== Profile File Format [[Config-Profile-Format]] + +Configuration profile provides similar options to the configuration builder. +The profile file must contain at least the list of sources from which configuration can be loaded. + +The root `sources` property contains an array (ordered) of objects defining each config source to +be used. +Each element of the array must contain at least the `type` property, determining the +config source type (such as `system-properties`, `file`). It may also contain a `properties` +property with additional configuration of the config source. + +An example development profile using "inlined" configuration: +[source,yaml] +.Config profile `config-profile-dev.yaml` +---- +sources: + - type: "inlined" + properties: + app.greeting: "Hello World" +---- + +An example of a profile using environment variables, system properties, classpath, and file configuration: +[source,yaml] +.Config profile `config-profile-prod.yaml` +---- +sources: + - type: "environment-variables" + - type: "system-properties" + - type: "file" + properties: + path: "config/config-prod.yaml" + - type: "classpath" + properties: + resource: "application.yaml" +---- + +==== Built-in Types +The config system supports these built-in types: + +.Built-in Types +|=== +|Type |Use |Related `ConfigSources` Method |Required Properties + +|`system-properties` |System properties are a config source |`ConfigSources.systemProperties()` | n/a +|`environment-variables` |Environment variables are a config source |`ConfigSources.environmentVariables()` | n/a +|`classpath` |Specified resource is used as a config source |`ConfigSources.classpath(String)` | `resource` - path to the resource to load +|`file` |Specified file is used as a config source |`ConfigSources.file(Path)` |`path` - path to the file to load +|`directory` |Each file in directory used as config entry, with key = file name and value = file contents |`ConfigSources.directory(String)` |`path` - path to the directory to use +|`url` |Specified URL is read as a config source |`ConfigSources.url(URL)` | `url` - URL from which to load the config +|`inlined` |The whole configuration tree under `properties` is added as a configuration source (excluding the `properties` node) |n/a |n/a +|`prefixed` |Associated config source is loaded with the specified prefix |`ConfigSources.prefixed(String,Supplier)` a|* `key` - key of config element in associated source to load +* `type` - associated config source specification +* `properties` - as needed to further qualify the associated config source +|=== + +Except for the `system-properties` and `environment-variables` types, the profile +`properties` section for a source can also specify any optional settings for the +corresponding config source type. The JavaDoc for the related config source +type builders lists the supported properties for each type. (For example, +link:{javadoc-base-url-api}/internal/FileConfigSource.FileBuilder.html[`FileConfigSource.FileBuilder`].) + +Here is an example profile in YAML format. Note how the `properties` sections +are at the same level as the `type` or `class` within a `sources` array entry. + +[source,yaml] +.Profile `config-profile.yaml` illustrating all built-in sources available on the classpath +---- +caching.enabled: false +sources: + - type: "system-properties" + - type: "environment-variables" + - type: "directory" + properties: + path: "conf/secrets" + media-type-mapping: + yaml: "application/x-yaml" + password: "application/base64" + polling-strategy: + type: "regular" + properties: + interval: "PT15S" + - type: "url" + properties: + url: "http://config-service/my-config" + media-type: "application/hocon" + optional: true + retry-policy: + type: "repeat" + properties: + retries: 3 + - type: "file" + properties: + optional: true + path: "conf/env.yaml" + change-watcher: + type: "file" + properties: + delay-millis: 5000 + - type: "prefixed" + properties: + key: "app" + type: "classpath" + properties: + resource: "app.conf" + - type: "classpath" + properties: + resource: "application.conf" +---- + +Note that the example shows how your profile can configure optional features such as polling +strategies and retry policies for config sources. + +==== Support for Custom Sources +Profiles can be used to set up custom config sources as well as the built-in ones described above. + +Implement the `ConfigSourceProvider` +[source,java] +---- +public class MyConfigSourceProvider implements ConfigSourceProvider { + private static final String TYPE = "my-type"; + + @Override + public boolean supports(String type) { + return TYPE.equals(type); + } + + @Override + public ConfigSource create(String type, Config metaConfig) { + // as we only support one in this implementation, we can just return it + return MyConfigSource.create(metaConfig); + } + + @Override + public Set supported() { + return Collections.singleton(TYPE); + } +} +---- + +Register it as a java service loader service +[source] +.File `META-INF/services/io.helidon.config.spi.ConfigSourceProvider` +---- +io.helidon.examples.MyConfigSourceProvider +---- + +And in `module-info.java` if using JPMS: +[source,java] +.File `module-info.java` +---- +provides io.helidon.config.spi.ConfigSourceProvider with io.helidon.examples.MyConfigSourceProvider +---- + +Now you can use the following profile: +[source,yaml] +---- +sources: + - type: "system-properties" + - type: "environment-variables" + - type: "my-type" + properties: + my-property: "some-value" +---- + +Note that it is the `io.helidon.config.AbstractConfigSource` class that provides support for +polling strategies, change watchers, and retry policies. If you create custom config sources that +should also offer this support be sure they extend `AbstractConfigSource` and implement appropriate +SPI interfaces (such as `io.helidon.config.spi.WatchableSource`) to support such features. + +==== Support for Custom Polling Strategies, Change Watchers, and Retry Policies + +Your config profile can include the set-up for polling strategies, change watchers, and retry +policies if the config source supports them. Declare them in a way similar to +how you declare the config sources themselves: by `type` and with +accompanying `properties`. + +.Config Profile Support for Built-in Polling Strategies +|=== +|Strategy Type |Usage |Properties + +|`regular` +| Periodic polling - See link:{javadoc-base-url-api}/PollingStrategies.html#regular-java.time.Duration-[`PollingStrategies.regular`] method +|`interval` (`Duration`) - indicating how often to poll; e.g., `PT15S` represents 15 seconds + +|=== + +.Config Profile Support for Built-in Change Watchers +|=== +|Type |Usage |Properties + +|`file` +| Filesystem monitoring - See link:{javadoc-base-url-api}/PollingStrategies.html#watch-java.nio.file.Path-[`PollingStrategies.watch`] method +| `initial-delay-millis` - delay between the start of the watcher and first check for changes + +|=== + +.Config Profile Support for Built-in Retry Policies +|=== +|Policy Type |Usage |Properties + +|`repeat` +|Regularly-scheduled - see link:{javadoc-base-url-api}RetryPolicies.html#repeat-int-[`RetryPolicies.repeat`]. +a|`retries` (`int`) - number of retries to perform + + +Optional: + +* `delay` (`Duration`) - initial delay between retries +* `delay-factor` (`double`) - `delay` is repeatedly multiplied by this each retry to compute +the delay for each successive retry +* `call-timeout` (`Duration`) - timeout for a single invocation to load the source +* `overall-timeout` (`Duration`) - total timeout for all retry calls and delays +|=== + +To specify a custom polling strategy or custom retry policy, implement the interface +(`io.helidon.config.spi.PollingStrategy`, `io.helidon.config.spi.ChangeWatcher`, + or `io.helidon.config.spi.RetryPolicy`), and then implement the provider interface +(`io.helidon.config.spi.PollingStrategyProvider`, `io.helidon.config.spi.ChangeWatcherProvider`, or +`io.helidon.config.spi.RetryPolicyProvider`) to enable your custom implementations for +profiles. +You can then use any custom properties - these are provided as a `Config` instance to +the `create` method of the Provider implementation. + +See link:{javadoc-base-url-api}/spi/RetryPolicy.html[`RetryPolicy`], +link:{javadoc-base-url-api}/spi/RetryPolicy.html[`ChangeWatcher`], and +link:{javadoc-base-url-api}/spi/PollingStrategy.html[`PollingStrategy`] JavaDoc +sections. \ No newline at end of file diff --git a/docs/se/guides/03_config.adoc b/docs/se/guides/03_config.adoc index 073cfe40cad..17e6284fb80 100644 --- a/docs/se/guides/03_config.adoc +++ b/docs/se/guides/03_config.adoc @@ -39,7 +39,7 @@ Helidon provides a very flexible and comprehensive configuration system, offerin You can include configuration data from a variety of sources using different formats, like JSON and YAML. Furthermore, you can customize the precedence of sources and make them optional or mandatory. This guide introduces Helidon SE configuration and demonstrates the fundamental concepts using several examples. -Refer to <> for the full configuration concepts documentation. +Refer to <> for the full configuration concepts documentation. === Create a sample Helidon SE project @@ -111,7 +111,7 @@ NOTE: Because environment variable names are restricted to alphanumeric characte Helidon adds aliases to the environment configuration source, allowing entries with dotted and/or hyphenated keys to be overriden. For example, this mapping allows an environment variable named "APP_GREETING" to override an entry key named "app.greeting". In the same way, an environment variable named "APP_dash_GREETING" will map to -"app-greeting". See <> for more information. +"app-greeting". See <> for more information. The following examples will demonstrate the default precedence order. @@ -210,7 +210,7 @@ and the contents of that file are used as the corresponding config String value. 6. A URL resource - contents is parsed according to its inferred format. You can also define custom sources, such as Git, and use them in your Helidon application. -See <> for more information. +See <> for more information. === Classpath sources @@ -440,14 +440,19 @@ curl http://localhost:8080/greet } ---- -=== Meta-configuration +=== Configuration Profiles -Instead of directly specifying the configuration sources in your code, you can use meta-configuration in a file that declares -the configuration sources and their attributes. This requires using the `Config.loadSourcesFrom` method rather than a `Config.Buider` -object. The contents of the meta-configuration file needs to be in JSON, YAML, or HOCON format. YAML is used in the following example. +Instead of directly specifying the configuration sources in your code, you can use a profile file that declares +the configuration sources and their attributes. -[source,bash] -.Create a file named `meta-config.yaml` in the `helidon-quickstart-se` directory with the following contents: +Simplest way to use a profile is to define a `config-profile.yaml` (and possible other files, such as +`config-profile-dev.yaml` for `dev` profile) on classpath or on file system, and create config using `Config.create()`. +The profile can be changed by a system property `config.profile`, or using an environment variable `HELIDON_CONFIG_PROFILE`. + +Profile file can use any supported format, following example is using `YAML`. + +[source,yaml] +.Create a file named `cofnig-profile.yaml` in the `helidon-quickstart-se` directory with the following contents: ---- sources: - type: "classpath" // <1> @@ -465,7 +470,7 @@ sources: return Config.create(); // <1> } ---- -<1> Will use `meta-config.yaml` by default +<1> Will use `config-profile.yaml` by default [source,bash] .Build and run the application, then invoke the endpoint and check the response: @@ -478,11 +483,11 @@ curl http://localhost:8080/greet ---- <1> The `application.yaml` resource file was used to get the greeting. -The source precedence order in a meta-configuration file is the order of appearance in the file. +The source precedence order in a profile file is the order of appearance in the file. This is demonstrated below where the `config-file.properties` has highest precedence. -[source,bash] -.Replace the contents of the `meta-config.yaml` file: +[source,yaml] +.Replace the contents of the `config-profile.yaml` file: ---- sources: - type: "file" // <1> @@ -511,11 +516,11 @@ curl http://localhost:8080/greet ---- <1> The `config-file.properties` source now takes precedence. -When using a meta-config file, you need to explicitly include both environment variables and system properties as +When using a profile file, you need to explicitly include both environment variables and system properties as a source if you want to use them. [source,bash] -.Replace the contents of the `meta-config.yaml` file: +.Replace the contents of the `config-profile.yaml` file: ---- sources: - type: "environment-variables" // <1> @@ -531,8 +536,8 @@ sources: <2> System properties are now used as a source. -You can re-run the previous tests that exercised environment variables and system properties. Swap the two types to see -the precedence change. Be sure to unset APP_GREETING after you finish testing. +You can re-run the previous tests that exercised environment variables and system properties. Swap the two types to see +the precedence change. Be sure to unset APP_GREETING after you finish testing. == Accessing Config within an application @@ -542,7 +547,7 @@ to translate the source into an in-memory tree which represents the configuratio of methods to access in-memory configuration. These can be categorized as _key access_ or _tree navigation_. You have been using _key access_ for all of the examples to this point. For example `app.greeting` is accessing the `greeting` child node of the `app` parent node. There are many options for access this data using navigation -methods as described in <> and <>. +methods as described in <> and <>. === Accessing config using keys or navigation @@ -587,7 +592,7 @@ configuration tree. The example below shows how to get the `greeting` node when the `app` subtree. [source,bash] -.Replace the contents of the `meta-config.yaml` file: +.Replace the contents of the `config-profile.yaml` file: ---- sources: - type: "classpath" @@ -644,18 +649,18 @@ curl http://localhost:8080/greet Even though in-memory config trees are immutable, the config system internally records configuration source metadata that allows it to watch sources for changes. Your application listens for updates to the underlying config sources and reacts to the changes. -See <> for a full discussion on this topic. +See <> for a full discussion on this topic. The following example demonstrates how to listen and react to configuration changes. -[source,bash] -.Replace the contents of the `meta-config.yaml` file: +[source,yaml] +.Replace the contents of the `config-profile.yaml` file: ---- sources: - type: "file" properties: path: "./config-file.properties" - polling-strategy: - type: "watch" + change-watcher: + type: "file" - type: "classpath" properties: resource: "application.yaml" @@ -877,7 +882,7 @@ kubectl delete configmap helidon-configmap == Summary This guide has demonstrated how to use basic Helidon configuration features. The full configuration documentation, starting with the -introduction section at <> has much more information including +introduction section at <> has much more information including the following: - Architecture diff --git a/examples/config/pom.xml b/examples/config/pom.xml index 0c9a3ba6d30..c1b04147313 100644 --- a/examples/config/pom.xml +++ b/examples/config/pom.xml @@ -37,6 +37,7 @@ mapping overrides sources + profiles diff --git a/examples/config/profiles/README.md b/examples/config/profiles/README.md new file mode 100644 index 00000000000..8c98e3308cc --- /dev/null +++ b/examples/config/profiles/README.md @@ -0,0 +1,31 @@ +# Helidon Config Profiles Example + +This example shows how to load configuration from multiple +configuration sources using profiles. + +This example contains the following profiles: + +1. no profile - if you start the application with no profile, the usual `src/main/resources/application.yaml` will be used +2. `local` - `src/main/resources/application-local.yaml` will be used +3. `dev` - has an explicit profile file `config-profile-dev.yaml` on classpath that defines an inlined configuration +4. `stage` - has an explicit profile file `config-profile-stage.yaml` on classpath that defines a classpath config source +4. `prod` - has an explicit profile file `config-profile-prod.yaml` on file system that defines a path config source + +To switch profiles +- either use a system property `config.profile` +- or use an environment variable `HELIDON_CONFIG_PROFILE` + + +## How to run this example: + +Build the application +```shell +mvn clean package +``` + +Run it with a profile +```shell +java -Dconfig.profile=prod -jar target/helidon-examples-config-profiles.jar +``` + +Changing the profile name should use different configuration. \ No newline at end of file diff --git a/examples/config/profiles/config-profile-prod.yaml b/examples/config/profiles/config-profile-prod.yaml new file mode 100644 index 00000000000..f0cf375cc49 --- /dev/null +++ b/examples/config/profiles/config-profile-prod.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +sources: + - type: "environment-variables" + - type: "system-properties" + - type: "file" + properties: + path: "config/config-prod.yaml" diff --git a/examples/config/profiles/config/config-prod.yaml b/examples/config/profiles/config/config-prod.yaml new file mode 100644 index 00000000000..ee020122c09 --- /dev/null +++ b/examples/config/profiles/config/config-prod.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +message: "config/config-prod.yaml" diff --git a/examples/config/profiles/pom.xml b/examples/config/profiles/pom.xml new file mode 100644 index 00000000000..0ce893d5eb7 --- /dev/null +++ b/examples/config/profiles/pom.xml @@ -0,0 +1,65 @@ + + + + + 4.0.0 + + io.helidon.applications + helidon-se + 2.3.2-SNAPSHOT + ../../../applications/se/pom.xml + + io.helidon.examples.config + helidon-examples-config-profiles + Helidon Config Examples Profiles + + + The example shows how to use Config Profiles (meta configuration). + + + + io.helidon.examples.config.profiles.Main + + + + + io.helidon.config + helidon-config + + + io.helidon.config + helidon-config-yaml + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + + diff --git a/examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/Main.java b/examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/Main.java new file mode 100644 index 00000000000..0077a16939d --- /dev/null +++ b/examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/Main.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +package io.helidon.examples.config.profiles; + +import io.helidon.config.Config; + +/** + * Example main class. + */ +public final class Main { + private Main() { + } + + /** + * Main method. + * + * @param args ignored + */ + public static void main(String[] args) { + Config config = Config.create(); + + System.out.println("Configured message: " + config.get("message").asString().orElse("MISSING")); + } +} diff --git a/examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/package-info.java b/examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/package-info.java new file mode 100644 index 00000000000..bb7cb9ee79e --- /dev/null +++ b/examples/config/profiles/src/main/java/io/helidon/examples/config/profiles/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Example showing usage of configuration profiles in Helidon SE. + */ +package io.helidon.examples.config.profiles; diff --git a/examples/config/profiles/src/main/resources/application-local.yaml b/examples/config/profiles/src/main/resources/application-local.yaml new file mode 100644 index 00000000000..f54597e9d16 --- /dev/null +++ b/examples/config/profiles/src/main/resources/application-local.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +message: "src/main/resources/application-local.yaml" diff --git a/examples/config/profiles/src/main/resources/application-stage.yaml b/examples/config/profiles/src/main/resources/application-stage.yaml new file mode 100644 index 00000000000..7343ff57108 --- /dev/null +++ b/examples/config/profiles/src/main/resources/application-stage.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +message: "src/main/resources/application-stage.yaml" diff --git a/examples/config/profiles/src/main/resources/application.yaml b/examples/config/profiles/src/main/resources/application.yaml new file mode 100644 index 00000000000..3eee73c1582 --- /dev/null +++ b/examples/config/profiles/src/main/resources/application.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +message: "src/main/resources/application.yaml" diff --git a/examples/config/profiles/src/main/resources/config-profile-dev.yaml b/examples/config/profiles/src/main/resources/config-profile-dev.yaml new file mode 100644 index 00000000000..345d2f00304 --- /dev/null +++ b/examples/config/profiles/src/main/resources/config-profile-dev.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +sources: + - type: "inlined" + properties: + message: "inlined in dev profile" diff --git a/examples/config/profiles/src/main/resources/config-profile-stage.yaml b/examples/config/profiles/src/main/resources/config-profile-stage.yaml new file mode 100644 index 00000000000..9740180c872 --- /dev/null +++ b/examples/config/profiles/src/main/resources/config-profile-stage.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +sources: + - type: "classpath" + properties: + resource: "application-stage.yaml"