From 84451b44b5624a1e9773692e1a636ebd6d55bda0 Mon Sep 17 00:00:00 2001 From: Keith Lustria <34235093+klustria@users.noreply.github.com> Date: Fri, 20 May 2022 10:29:15 -0700 Subject: [PATCH] Support Hocon/Json Configuration Source for MP (#4218) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support Hocon/Json Configuration Source for MP 1. Meta-config is refactored to allow it to be customizable by users for other types. 2. Hocon/Json is parsed as a config source only via meta-config. * Create hocon includer per instance of hocon config source and add justification comment for security bug filtering * Remove support of application.conf/json hocon/json as default discoverable sources * Initial commit for Mp config-source provider implementation * Add MP meta-config provider test for Environment Variables * Add Yaml MP meta-config provider tests and various resolution to review feedback * Add helidon-config-yaml-mp as a dependency of helidon-microprofile-cdi * Declare "uses io.helidon.config.mp.spi.MpMetaConfigProvider" in config-mp’s module-info and set priority of all implementations of MpMetaConfigProvider to 300 * Various updates/cleanups from review feedback * Add helidon-config-yaml dependency to metrics examples after they were failing because of dependency on yaml-mp --- bom/pom.xml | 5 + config/config-mp/pom.xml | 4 - .../io/helidon/config/mp/MpConfigBuilder.java | 240 +++-------- ...nvironmentVariablesMetaConfigProvider.java | 46 +++ .../helidon/config/mp/MpMetaConfigUtils.java | 205 ++++++++++ .../mp/MpPropertiesMetaConfigProvider.java | 53 +++ .../MpSystemPropertiesMetaConfigProvider.java | 46 +++ .../config/mp/spi/MpMetaConfigProvider.java | 47 +++ .../config-mp/src/main/java/module-info.java | 6 +- .../helidon/config/mp/MpMetaConfigTest.java | 63 ++- ...p-meta-config-classpath-profile.properties | 22 ++ ...tom-mp-meta-config-not-optional.properties | 23 ++ .../custom-mp-meta-config-ordinal.properties | 25 ++ ...tom-mp-meta-config-path-profile.properties | 22 ++ ...mp-meta-config-sysprops-envvars.properties | 21 + .../meta-config/classpath-dev.properties | 17 + .../resources/meta-config/path-dev.properties | 17 + config/hocon-mp/etc/spotbugs/exclude.xml | 38 ++ config/hocon-mp/pom.xml | 70 ++++ .../hocon/mp/HoconMpConfigIncluder.java | 134 +++++++ .../config/hocon/mp/HoconMpConfigSource.java | 374 ++++++++++++++++++ .../hocon/mp/HoconMpMetaConfigProvider.java | 54 +++ .../helidon/config/hocon/mp/package-info.java | 22 ++ .../hocon-mp/src/main/java/module-info.java | 32 ++ .../native-image.properties | 17 + .../hocon/mp/HoconJsonMpConfigSourceTest.java | 89 +++++ .../hocon/mp/HoconJsonMpMetaConfigTest.java | 158 ++++++++ .../src/test/resources/application.conf | 28 ++ .../src/test/resources/application.json | 12 + .../resources/custom-application-dev.conf | 21 + .../test/resources/custom-application.conf | 25 ++ .../test/resources/custom-application.json | 10 + .../test/resources/custom-include-dev.conf | 24 ++ .../src/test/resources/custom-include.conf | 26 ++ ...p-meta-config-hocon-classpath-profile.yaml | 24 ++ ...custom-mp-meta-config-hocon-classpath.yaml | 23 ++ ...p-meta-config-hocon-path-not-optional.yaml | 23 ++ ...tom-mp-meta-config-hocon-path-profile.yaml | 24 ++ .../custom-mp-meta-config-hocon-path.yaml | 23 ++ .../resources/custom-mp-meta-config-json.yaml | 23 ++ .../hocon-mp/src/test/resources/include.conf | 26 ++ .../ordinal-mp-meta-config-hocon.yaml | 27 ++ .../hocon-mp/src/test/resources/ordinal.conf | 25 ++ .../hocon-mp/src/test/resources/ordinal.json | 8 + config/pom.xml | 1 + config/yaml-mp/pom.xml | 4 + .../yaml/mp/YamlMetaConfigProvider.java | 54 +++ .../config/yaml/mp/YamlMpConfigSource.java | 5 +- config/yaml-mp/src/main/java/module-info.java | 6 +- .../yaml/mp/YamlMpConfigSourceTest.java | 35 +- .../config/yaml/mp/YamlMpMetaConfigTest.java | 134 +++++++ .../src/test/resources/application.yaml | 25 ++ .../resources/custom-application-dev.yaml | 20 + .../test/resources/custom-application.yaml | 24 ++ ...tom-mp-meta-config-classpath-profile.yaml} | 17 +- .../custom-mp-meta-config-classpath.yaml | 23 ++ ...stom-mp-meta-config-path-not-optional.yaml | 23 ++ .../custom-mp-meta-config-path-profile.yaml | 24 ++ .../resources/custom-mp-meta-config-path.yaml | 23 ++ .../yaml-mp/src/test/resources/ordinal-2.yaml | 24 ++ .../resources/ordinal-mp-meta-config.yaml | 27 ++ .../yaml-mp/src/test/resources/ordinal.yaml | 22 ++ examples/metrics/exemplar/pom.xml | 4 + examples/metrics/filtering/se/pom.xml | 4 + examples/metrics/kpi/pom.xml | 4 + microprofile/cdi/pom.xml | 6 +- tests/integration/config/hocon-mp/pom.xml | 61 +++ .../helidon/config/hocon/mp/ConfigBean.java | 98 +++++ .../hocon/mp/HoconJsonMpMetaConfigTest.java | 95 +++++ .../src/test/resources/application-dev.conf | 26 ++ .../src/test/resources/application-dev.json | 10 + .../src/test/resources/application.conf | 28 ++ .../src/test/resources/application.json | 12 + .../test/resources/custom-mp-meta-config.yaml | 25 ++ .../src/test/resources/include-dev.conf | 26 ++ .../hocon-mp/src/test/resources/include.conf | 26 ++ .../src/test/resources/meta-application.conf | 28 ++ .../src/test/resources/meta-application.json | 12 + .../src/test/resources/meta-include.conf | 26 ++ tests/integration/config/pom.xml | 3 +- 80 files changed, 2976 insertions(+), 231 deletions(-) create mode 100644 config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesMetaConfigProvider.java create mode 100644 config/config-mp/src/main/java/io/helidon/config/mp/MpMetaConfigUtils.java create mode 100644 config/config-mp/src/main/java/io/helidon/config/mp/MpPropertiesMetaConfigProvider.java create mode 100644 config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesMetaConfigProvider.java create mode 100644 config/config-mp/src/main/java/io/helidon/config/mp/spi/MpMetaConfigProvider.java create mode 100644 config/config-mp/src/test/resources/custom-mp-meta-config-classpath-profile.properties create mode 100644 config/config-mp/src/test/resources/custom-mp-meta-config-not-optional.properties create mode 100644 config/config-mp/src/test/resources/custom-mp-meta-config-ordinal.properties create mode 100644 config/config-mp/src/test/resources/custom-mp-meta-config-path-profile.properties create mode 100644 config/config-mp/src/test/resources/custom-mp-meta-config-sysprops-envvars.properties create mode 100644 config/config-mp/src/test/resources/meta-config/classpath-dev.properties create mode 100644 config/config-mp/src/test/resources/meta-config/path-dev.properties create mode 100644 config/hocon-mp/etc/spotbugs/exclude.xml create mode 100644 config/hocon-mp/pom.xml create mode 100644 config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigIncluder.java create mode 100644 config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigSource.java create mode 100644 config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpMetaConfigProvider.java create mode 100644 config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/package-info.java create mode 100644 config/hocon-mp/src/main/java/module-info.java create mode 100644 config/hocon-mp/src/main/resources/META-INF/native-image/io.helidon.config/helidon-config-hocon-mp/native-image.properties create mode 100644 config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpConfigSourceTest.java create mode 100644 config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java create mode 100644 config/hocon-mp/src/test/resources/application.conf create mode 100644 config/hocon-mp/src/test/resources/application.json create mode 100644 config/hocon-mp/src/test/resources/custom-application-dev.conf create mode 100644 config/hocon-mp/src/test/resources/custom-application.conf create mode 100644 config/hocon-mp/src/test/resources/custom-application.json create mode 100644 config/hocon-mp/src/test/resources/custom-include-dev.conf create mode 100644 config/hocon-mp/src/test/resources/custom-include.conf create mode 100644 config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath-profile.yaml create mode 100644 config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath.yaml create mode 100644 config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-not-optional.yaml create mode 100644 config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-profile.yaml create mode 100644 config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path.yaml create mode 100644 config/hocon-mp/src/test/resources/custom-mp-meta-config-json.yaml create mode 100644 config/hocon-mp/src/test/resources/include.conf create mode 100644 config/hocon-mp/src/test/resources/ordinal-mp-meta-config-hocon.yaml create mode 100644 config/hocon-mp/src/test/resources/ordinal.conf create mode 100644 config/hocon-mp/src/test/resources/ordinal.json create mode 100644 config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMetaConfigProvider.java create mode 100644 config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpMetaConfigTest.java create mode 100644 config/yaml-mp/src/test/resources/application.yaml create mode 100644 config/yaml-mp/src/test/resources/custom-application-dev.yaml create mode 100644 config/yaml-mp/src/test/resources/custom-application.yaml rename config/{config-mp/src/test/resources/custom-mp-meta-config.yaml => yaml-mp/src/test/resources/custom-mp-meta-config-classpath-profile.yaml} (60%) create mode 100644 config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath.yaml create mode 100644 config/yaml-mp/src/test/resources/custom-mp-meta-config-path-not-optional.yaml create mode 100644 config/yaml-mp/src/test/resources/custom-mp-meta-config-path-profile.yaml create mode 100644 config/yaml-mp/src/test/resources/custom-mp-meta-config-path.yaml create mode 100644 config/yaml-mp/src/test/resources/ordinal-2.yaml create mode 100644 config/yaml-mp/src/test/resources/ordinal-mp-meta-config.yaml create mode 100644 config/yaml-mp/src/test/resources/ordinal.yaml create mode 100644 tests/integration/config/hocon-mp/pom.xml create mode 100644 tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/ConfigBean.java create mode 100644 tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java create mode 100644 tests/integration/config/hocon-mp/src/test/resources/application-dev.conf create mode 100644 tests/integration/config/hocon-mp/src/test/resources/application-dev.json create mode 100644 tests/integration/config/hocon-mp/src/test/resources/application.conf create mode 100644 tests/integration/config/hocon-mp/src/test/resources/application.json create mode 100644 tests/integration/config/hocon-mp/src/test/resources/custom-mp-meta-config.yaml create mode 100644 tests/integration/config/hocon-mp/src/test/resources/include-dev.conf create mode 100644 tests/integration/config/hocon-mp/src/test/resources/include.conf create mode 100644 tests/integration/config/hocon-mp/src/test/resources/meta-application.conf create mode 100644 tests/integration/config/hocon-mp/src/test/resources/meta-application.json create mode 100644 tests/integration/config/hocon-mp/src/test/resources/meta-include.conf diff --git a/bom/pom.xml b/bom/pom.xml index 6c6c906467a..7c9b34dff74 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -256,6 +256,11 @@ helidon-config-yaml-mp ${helidon.version} + + io.helidon.config + helidon-config-hocon-mp + ${helidon.version} + io.helidon.config helidon-config-git diff --git a/config/config-mp/pom.xml b/config/config-mp/pom.xml index da813c6b459..276928d5c02 100644 --- a/config/config-mp/pom.xml +++ b/config/config-mp/pom.xml @@ -54,10 +54,6 @@ org.eclipse.microprofile.config microprofile-config-api - - io.helidon.config - helidon-config-yaml-mp - org.junit.jupiter junit-jupiter-api diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java index 910369f6e9d..1da5efab11d 100644 --- a/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 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. @@ -24,7 +24,6 @@ import java.net.URI; import java.net.URL; import java.nio.charset.Charset; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; @@ -47,6 +46,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; @@ -54,8 +54,6 @@ import java.util.SimpleTimeZone; import java.util.TimeZone; import java.util.UUID; -import java.util.function.BiFunction; -import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -66,7 +64,7 @@ import io.helidon.config.ConfigMappers; import io.helidon.config.ConfigValue; import io.helidon.config.mp.spi.MpConfigFilter; -import io.helidon.config.yaml.mp.YamlMpConfigSource; +import io.helidon.config.mp.spi.MpMetaConfigProvider; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.spi.ConfigBuilder; @@ -79,11 +77,31 @@ /** * Configuration builder. */ -@Deprecated -public class MpConfigBuilder implements ConfigBuilder { +class MpConfigBuilder implements ConfigBuilder { private static final Logger LOGGER = Logger.getLogger(MpConfigBuilder.class.getName()); private static final String DEFAULT_CONFIG_SOURCE = "META-INF/microprofile-config.properties"; + private static final Map MP_META_PROVIDERS; + static { + List mpMetaConfigProviders = + HelidonServiceLoader.builder(ServiceLoader.load(MpMetaConfigProvider.class)) + .addService(new MpEnvironmentVariablesMetaConfigProvider()) + .addService(new MpSystemPropertiesMetaConfigProvider()) + .addService(new MpPropertiesMetaConfigProvider()) + .build() + .asList(); + + Map theMap = new HashMap<>(); + // ordered by priority + for (MpMetaConfigProvider mpMetaConfigProvider : mpMetaConfigProviders) { + for (String supportedType : mpMetaConfigProvider.supportedTypes()) { + theMap.putIfAbsent(supportedType, mpMetaConfigProvider); + } + } + MP_META_PROVIDERS = Map.copyOf(theMap); + } + + private final List sources = new LinkedList<>(); private final List converters = new LinkedList<>(); @@ -154,14 +172,6 @@ private static Class doGetType(Class clazz) { return doGetType(clazz.getSuperclass()); } - private static String toProfileName(String fileName, String profile) { - int i = fileName.lastIndexOf('.'); - if (i > -1) { - return fileName.substring(0, i) + "-" + profile + fileName.substring(i); - } - return fileName + "-" + profile; - } - @Override public ConfigBuilder addDefaultSources() { useDefaultSources = true; @@ -237,25 +247,14 @@ private void processMetaSources(List configs) { for (io.helidon.config.Config config : configs) { String type = config.get("type").asString() .orElseThrow(() -> new ConfigException("Meta configuration sources must have a \"type\" property defined")); - // in MP, we have a hardcoded list of supported configuration source types - List delegates; - switch (type) { - case "system-properties": - delegates = List.of(MpConfigSources.systemProperties()); - break; - case "environment-variables": - delegates = List.of(MpConfigSources.environmentVariables()); - break; - case "properties": - delegates = propertiesSource(config); - break; - case "yaml": - delegates = yamlSource(config); - break; - default: - throw new ConfigException("Meta configuration source type \"" + type + "\" is not supported. Use on of: " - + "system-properties, environment-variables, properties, yaml"); + MpMetaConfigProvider mpMetaConfigProvider = MP_META_PROVIDERS.get(type); + if (mpMetaConfigProvider == null) { + throw new ConfigException("Wrong meta configuration, type " + type + + " not supported, only supporting: " + MP_META_PROVIDERS.keySet()); } + + List delegates = mpMetaConfigProvider.create(type, config, profile); + boolean shouldCount = delegates.size() > 1; int counter = 0; @@ -279,163 +278,6 @@ private void processMetaSources(List configs) { } } - private List propertiesSource(io.helidon.config.Config config) { - return sourceFromMeta(config, - MpConfigSources::create, - MpConfigSources::classPath, - MpConfigSources::classPath, - MpConfigSources::create); - } - - private List yamlSource(io.helidon.config.Config config) { - return sourceFromMeta(config, - YamlMpConfigSource::create, - YamlMpConfigSource::classPath, - YamlMpConfigSource::classPath, - YamlMpConfigSource::create); - } - - private List sourceFromMeta(io.helidon.config.Config config, - Function fromPath, - Function> fromClasspath, - BiFunction> fromClasspathWithProfile, - Function fromUrl) { - - boolean optional = config.get("optional").asBoolean().orElse(false); - - String location; - Exception cause = null; - - ConfigValue pathConfig = config.get("path").as(Path.class); - if (pathConfig.isPresent()) { - Path path = pathConfig.get(); - List result = sourceFromPathMeta(path, fromPath); - - if (!result.isEmpty()) { - return result; - } - // else the file was not found, check optional - location = "path " + path.toAbsolutePath(); - } else { - ConfigValue classpathConfig = config.get("classpath").as(String.class); - if (classpathConfig.isPresent()) { - String classpath = classpathConfig.get(); - List sources; - - if (profile == null) { - sources = fromClasspath.apply(classpath); - } else { - sources = fromClasspathWithProfile.apply(classpath, profile); - } - - if (!sources.isEmpty()) { - return sources; - } - location = "classpath " + classpath; - } else { - ConfigValue urlConfig = config.get("url").as(URL.class); - if (urlConfig.isPresent()) { - URL url = urlConfig.get(); - List sources = null; - try { - sources = sourceFromUrlMeta(url, fromUrl); - } catch (ConfigException e) { - cause = e; - } - - if (sources != null && !sources.isEmpty()) { - return sources; - } - location = "url " + url; - } else { - throw new ConfigException("MP meta configuration does not contain config source location. Node: " + config - .key()); - } - } - } - - if (optional) { - return List.of(); - } - String message = "Meta configuration could not find non-optional config source on " + location; - if (cause == null) { - throw new ConfigException(message); - } else { - throw new ConfigException(message, cause); - } - } - - private List sourceFromUrlMeta(URL url, Function fromUrl) { - ConfigSource profileSource = null; - ConfigSource mainSource = null; - Exception cause = null; - - if (profile != null) { - try { - String profileUrl = toProfileName(url.toString(), profile); - profileSource = fromUrl.apply(new URL(profileUrl)); - } catch (Exception e) { - cause = e; - } - } - - try { - mainSource = fromUrl.apply(url); - if (cause != null) { - LOGGER.log(Level.FINEST, "Failed to load profile URL resource, succeeded loading main from " + url, cause); - } - } catch (ConfigException e) { - if (cause != null) { - e.addSuppressed(cause); - throw e; - } else { - if (profileSource == null) { - throw e; - } else { - LOGGER.log(Level.FINEST, "Did not find main URL config source from " + url + ", have profile source", e); - } - } - } - return composite(mainSource, profileSource); - } - - private List sourceFromPathMeta(Path path, Function fromPath) { - ConfigSource profileSource = null; - ConfigSource mainSource = null; - - if (profile != null) { - Path fileNamePath = path.getFileName(); - String fileName = (fileNamePath == null ? "" : fileNamePath.toString()); - fileName = toProfileName(fileName, profile); - Path profileSpecific = path.resolveSibling(fileName); - if (Files.exists(profileSpecific) && Files.isRegularFile(profileSpecific)) { - profileSource = fromPath.apply(profileSpecific); - } - } - - if (Files.exists(path) && Files.isRegularFile(path)) { - mainSource = fromPath.apply(path); - } - - // now handle profile - return composite(mainSource, profileSource); - } - - private List composite(ConfigSource mainSource, ConfigSource profileSource) { - // now handle profile - if (profileSource == null) { - if (mainSource == null) { - return List.of(); - } - return List.of(mainSource); - } - if (mainSource == null) { - return List.of(profileSource); - } - - return List.of(MpConfigSources.composite(profileSource, mainSource)); - } - @Override public Config build() { // the build method MUST NOT modify builder state, as it may be called more than once @@ -474,6 +316,10 @@ public Config build() { Collections.reverse(ordinalSources); Collections.reverse(ordinalConverters); + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.finest("The following config sources are used (ordered): " + ordinalSources); + } + List targetSources = new LinkedList<>(); HashMap, Converter> targetConverters = new HashMap<>(); @@ -484,6 +330,9 @@ public Config build() { // if we already have a profile configured, we have loaded it and can safely return if (profile != null) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Built MP config for profile " + profile); + } return result; } @@ -492,7 +341,12 @@ public Config build() { // nope, return the result if (configuredProfile == null) { + LOGGER.fine("Built MP config with no profile"); return result; + } else { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.finest("MP profile configured, rebuilding: " + configuredProfile); + } } // yes, update it and re-build with profile information @@ -545,6 +399,10 @@ private void addDefaultSources(List targetConfigSources) { .map(OrdinalSource::new) .forEach(targetConfigSources::add); } + + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.finest("The following default config sources discovered: " + targetConfigSources); + } } } @@ -627,7 +485,7 @@ private OrdinalSource(ConfigSource source) { } private OrdinalSource(ConfigSource source, int ordinal) { - this.ordinal = ordinal; + this.ordinal = findOrdinal(source, ordinal); this.source = source; } diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesMetaConfigProvider.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesMetaConfigProvider.java new file mode 100644 index 00000000000..852233612cd --- /dev/null +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesMetaConfigProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, 2022 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.mp; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Prioritized; +import io.helidon.config.Config; +import io.helidon.config.mp.spi.MpMetaConfigProvider; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Helidon MicroProfile meta-config provider for Environment Variables. + */ +class MpEnvironmentVariablesMetaConfigProvider implements MpMetaConfigProvider, Prioritized { + @Override + public Set supportedTypes() { + return Set.of("environment-variables"); + } + + @Override + public List create(String type, Config metaConfig, String profile) { + return List.of(MpConfigSources.environmentVariables()); + } + + @Override + public int priority() { + return 300; + } +} diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpMetaConfigUtils.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpMetaConfigUtils.java new file mode 100644 index 00000000000..87d1fbab7b5 --- /dev/null +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpMetaConfigUtils.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2022 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.mp; + +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import io.helidon.config.ConfigException; +import io.helidon.config.ConfigValue; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Utilities for Helidon MicroProfile Meta-Config implementation. + */ +public class MpMetaConfigUtils { + private static final Logger LOGGER = Logger.getLogger(MpMetaConfig.class.getName()); + + private MpMetaConfigUtils() { + } + + /** + * A utility for providing a list of configuration sources. + * + * @param config configuration properties from a meta-config type + * @param profile name of the profile to use or null if not used + * @param fromPath Function used to process a config specified by a filepath + * @param fromClasspath Function used to process a config specified by a classpath + * @param fromClasspathWithProfile BiFunction used to process a config specified by a classpath and a profile name + * @param fromUrl Function used to process a config specified by a Url + * + * @return list of configuration sources + */ + public static List sourceFromMeta(io.helidon.config.Config config, + String profile, + Function fromPath, + Function> fromClasspath, + BiFunction> fromClasspathWithProfile, + Function fromUrl) { + + boolean optional = config.get("optional").asBoolean().orElse(false); + + String location; + Exception cause = null; + + ConfigValue pathConfig = config.get("path").as(Path.class); + if (pathConfig.isPresent()) { + Path path = pathConfig.get(); + List result = sourceFromPathMeta(path, profile, fromPath); + + if (!result.isEmpty()) { + return result; + } + // else the file was not found, check optional + location = "path " + path.toAbsolutePath(); + } else { + ConfigValue classpathConfig = config.get("classpath").as(String.class); + if (classpathConfig.isPresent()) { + String classpath = classpathConfig.get(); + List sources; + + if (profile == null) { + sources = fromClasspath.apply(classpath); + } else { + sources = fromClasspathWithProfile.apply(classpath, profile); + } + + if (!sources.isEmpty()) { + return sources; + } + location = "classpath " + classpath; + } else { + ConfigValue urlConfig = config.get("url").as(URL.class); + if (urlConfig.isPresent()) { + URL url = urlConfig.get(); + List sources = null; + try { + sources = sourceFromUrlMeta(url, profile, fromUrl); + } catch (ConfigException e) { + cause = e; + } + + if (sources != null && !sources.isEmpty()) { + return sources; + } + location = "url " + url; + } else { + throw new ConfigException("MP meta configuration does not contain config source location. Node: " + config + .key()); + } + } + } + + if (optional) { + return List.of(); + } + String message = "Meta configuration could not find non-optional config source on " + location; + if (cause == null) { + throw new ConfigException(message); + } else { + throw new ConfigException(message, cause); + } + } + + private static List sourceFromUrlMeta(URL url, String profile, Function fromUrl) { + ConfigSource profileSource = null; + ConfigSource mainSource = null; + Exception cause = null; + + if (profile != null) { + try { + String profileUrl = toProfileName(url.toString(), profile); + profileSource = fromUrl.apply(new URL(profileUrl)); + } catch (Exception e) { + cause = e; + } + } + + try { + mainSource = fromUrl.apply(url); + if (cause != null) { + LOGGER.log(Level.FINEST, "Failed to load profile URL resource, succeeded loading main from " + url, cause); + } + } catch (ConfigException e) { + if (cause != null) { + e.addSuppressed(cause); + throw e; + } else { + if (profileSource == null) { + throw e; + } else { + LOGGER.log(Level.FINEST, "Did not find main URL config source from " + url + ", have profile source", e); + } + } + } + return composite(mainSource, profileSource); + } + + private static List sourceFromPathMeta(Path path, String profile, Function fromPath) { + ConfigSource profileSource = null; + ConfigSource mainSource = null; + + if (profile != null) { + Path fileNamePath = path.getFileName(); + String fileName = (fileNamePath == null ? "" : fileNamePath.toString()); + fileName = toProfileName(fileName, profile); + Path profileSpecific = path.resolveSibling(fileName); + if (Files.exists(profileSpecific) && Files.isRegularFile(profileSpecific)) { + profileSource = fromPath.apply(profileSpecific); + } + } + + if (Files.exists(path) && Files.isRegularFile(path)) { + mainSource = fromPath.apply(path); + } + + // now handle profile + return composite(mainSource, profileSource); + } + + private static List composite(ConfigSource mainSource, ConfigSource profileSource) { + // now handle profile + if (profileSource == null) { + if (mainSource == null) { + return List.of(); + } + return List.of(mainSource); + } + if (mainSource == null) { + return List.of(profileSource); + } + + return List.of(MpConfigSources.composite(profileSource, mainSource)); + } + + private static String toProfileName(String fileName, String profile) { + int i = fileName.lastIndexOf('.'); + if (i > -1) { + return fileName.substring(0, i) + "-" + profile + fileName.substring(i); + } + return fileName + "-" + profile; + } + + +} diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpPropertiesMetaConfigProvider.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpPropertiesMetaConfigProvider.java new file mode 100644 index 00000000000..01bf8c81916 --- /dev/null +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpPropertiesMetaConfigProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 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.mp; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Prioritized; +import io.helidon.config.Config; +import io.helidon.config.mp.spi.MpMetaConfigProvider; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Helidon MicroProfile meta-config provider for Properties configuration files. + */ +class MpPropertiesMetaConfigProvider implements MpMetaConfigProvider, Prioritized { + @Override + public Set supportedTypes() { + return Set.of("properties"); + } + + @Override + public List create(String type, Config metaConfig, String profile) { + return MpMetaConfigUtils.sourceFromMeta( + metaConfig, + profile, + MpConfigSources::create, + MpConfigSources::classPath, + MpConfigSources::classPath, + MpConfigSources::create + ); + } + + @Override + public int priority() { + return 300; + } +} diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesMetaConfigProvider.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesMetaConfigProvider.java new file mode 100644 index 00000000000..1911848dddb --- /dev/null +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesMetaConfigProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 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.mp; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Prioritized; +import io.helidon.config.Config; +import io.helidon.config.mp.spi.MpMetaConfigProvider; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Helidon MicroProfile meta-config provider for Systems Properties. + */ +class MpSystemPropertiesMetaConfigProvider implements MpMetaConfigProvider, Prioritized { + @Override + public Set supportedTypes() { + return Set.of("system-properties"); + } + + @Override + public List create(String type, Config metaConfig, String profile) { + return List.of(MpConfigSources.systemProperties()); + } + + @Override + public int priority() { + return 300; + } +} diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/spi/MpMetaConfigProvider.java b/config/config-mp/src/main/java/io/helidon/config/mp/spi/MpMetaConfigProvider.java new file mode 100644 index 00000000000..15cfc66ed68 --- /dev/null +++ b/config/config-mp/src/main/java/io/helidon/config/mp/spi/MpMetaConfigProvider.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 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.mp.spi; + +import java.util.List; +import java.util.Set; + +import io.helidon.config.Config; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Java Service loader interface for Meta-config providers. + */ +public interface MpMetaConfigProvider { + /** + * Set of supported types for a MicroProfile meta-config provider. + * + * @return meta-config provider types + */ + Set supportedTypes(); + + /** + * Create a list of configuration sources from a meta-config type. + * + * @param type type of the config source + * @param metaConfig configuration properties of a meta-config type + * @param profile name of the profile to use or null if not used + * + * @return list of config sources + */ + List create(String type, Config metaConfig, String profile); +} diff --git a/config/config-mp/src/main/java/module-info.java b/config/config-mp/src/main/java/module-info.java index f874fb4d7a7..93fb98c81d0 100644 --- a/config/config-mp/src/main/java/module-info.java +++ b/config/config-mp/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 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. @@ -21,7 +21,6 @@ requires java.logging; requires io.helidon.common; requires io.helidon.config; - requires io.helidon.config.yaml.mp; requires transitive microprofile.config.api; requires java.annotation; requires io.helidon.common.serviceloader; @@ -33,7 +32,8 @@ uses org.eclipse.microprofile.config.spi.ConfigSourceProvider; uses org.eclipse.microprofile.config.spi.Converter; uses io.helidon.config.mp.spi.MpConfigFilter; + uses io.helidon.config.mp.spi.MpMetaConfigProvider; uses io.helidon.config.spi.ConfigParser; provides org.eclipse.microprofile.config.spi.ConfigProviderResolver with io.helidon.config.mp.MpConfigProviderResolver; -} \ No newline at end of file +} diff --git a/config/config-mp/src/test/java/io/helidon/config/mp/MpMetaConfigTest.java b/config/config-mp/src/test/java/io/helidon/config/mp/MpMetaConfigTest.java index 3903c3e39df..c55697e9fcd 100644 --- a/config/config-mp/src/test/java/io/helidon/config/mp/MpMetaConfigTest.java +++ b/config/config-mp/src/test/java/io/helidon/config/mp/MpMetaConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 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. @@ -19,11 +19,13 @@ import java.util.LinkedList; import java.util.List; +import io.helidon.config.ConfigException; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.eclipse.microprofile.config.spi.ConfigSource; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,6 +34,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.iterableWithSize; + class MpMetaConfigTest { private static ConfigProviderResolver configResolver; private Config config; @@ -58,18 +61,16 @@ void resetConfig() { } @Test - void testMetaYaml() { - System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config.yaml"); + void testMetaEnvironmentVariablesSystemProperties() { + System.setProperty("property1", "value1"); + System.setProperty("property2", "value2"); + System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-sysprops-envvars.properties"); config = ConfigProvider.getConfig(); - - // validate the config sources - Iterable configSources = config.getConfigSources(); - List sourceNames = new LinkedList<>(); - configSources.forEach(it -> sourceNames.add(it.getName())); - - assertThat(sourceNames, iterableWithSize(2)); - assertThat(sourceNames.get(0), is("CLASSPATH")); - assertThat(config.getValue("value", String.class), is("classpath")); + assertThat(config.getValue("property1", String.class), is("value1")); + assertThat(config.getValue("property2", String.class), is("value2")); + assertThat(config.getValue("foo.bar", String.class), is("mapped-env-value")); + System.clearProperty("property1"); + System.clearProperty("property2"); } @Test @@ -77,6 +78,44 @@ void testMetaProperties() { System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config.properties"); config = ConfigProvider.getConfig(); assertThat(config.getValue("value", String.class), is("path")); + assertThat(config.getValue("config_ordinal", Integer.class), is(150)); + } + + @Test + void testMetaPropertiesOrdinal() { + System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, + "custom-mp-meta-config-ordinal.properties"); + config = ConfigProvider.getConfig(); + assertThat(config.getValue("value", String.class), is("classpath")); + assertThat(config.getValue("config_ordinal", Integer.class), is(125)); + } + + @Test + void testMetaPropertiesClassPathProfile() { + System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, + "custom-mp-meta-config-classpath-profile.properties"); + config = ConfigProvider.getConfig(); + assertThat(config.getValue("value", String.class), is("classpath_profile")); + assertThat(config.getValue("config_ordinal", Integer.class), is(125)); + } + + @Test + void testMetaPropertiesPathProfile() { + System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, + "custom-mp-meta-config-path-profile.properties"); + config = ConfigProvider.getConfig(); + assertThat(config.getValue("value", String.class), is("path_profile")); + assertThat(config.getValue("config_ordinal", Integer.class), is(150)); + } + + @Test + void testMetaPropertiesNotOptional() { + System.setProperty(MpMetaConfig.META_CONFIG_SYSTEM_PROPERTY, + "custom-mp-meta-config-not-optional.properties"); + try { + config = ConfigProvider.getConfig(); + Assertions.fail("Expecting meta-config to fail due to not optional non-existent config source"); + } catch (ConfigException e) {} } @Test diff --git a/config/config-mp/src/test/resources/custom-mp-meta-config-classpath-profile.properties b/config/config-mp/src/test/resources/custom-mp-meta-config-classpath-profile.properties new file mode 100644 index 00000000000..6a441c9300f --- /dev/null +++ b/config/config-mp/src/test/resources/custom-mp-meta-config-classpath-profile.properties @@ -0,0 +1,22 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources=false +add-discovered-converters=false +profile=dev + +sources.0.type=properties +sources.0.classpath=meta-config/classpath.properties diff --git a/config/config-mp/src/test/resources/custom-mp-meta-config-not-optional.properties b/config/config-mp/src/test/resources/custom-mp-meta-config-not-optional.properties new file mode 100644 index 00000000000..1b23a75dadb --- /dev/null +++ b/config/config-mp/src/test/resources/custom-mp-meta-config-not-optional.properties @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources=false +add-discovered-converters=false +profile=dev + +sources.0.type=properties +sources.0.path=src/test/resources/not_exist.properties +sources.0.optional=false diff --git a/config/config-mp/src/test/resources/custom-mp-meta-config-ordinal.properties b/config/config-mp/src/test/resources/custom-mp-meta-config-ordinal.properties new file mode 100644 index 00000000000..2c4eb706c38 --- /dev/null +++ b/config/config-mp/src/test/resources/custom-mp-meta-config-ordinal.properties @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources=false +add-discovered-converters=false + +sources.0.type=properties +sources.0.classpath=meta-config/classpath.properties +# Set to higher ordinality to force this to be processed last +sources.0.ordinal=500 +sources.1.type=properties +sources.1.path=src/test/resources/meta-config/path.properties diff --git a/config/config-mp/src/test/resources/custom-mp-meta-config-path-profile.properties b/config/config-mp/src/test/resources/custom-mp-meta-config-path-profile.properties new file mode 100644 index 00000000000..0b7c9699d2c --- /dev/null +++ b/config/config-mp/src/test/resources/custom-mp-meta-config-path-profile.properties @@ -0,0 +1,22 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources=false +add-discovered-converters=false +profile=dev + +sources.0.type=properties +sources.0.path=src/test/resources/meta-config/path.properties diff --git a/config/config-mp/src/test/resources/custom-mp-meta-config-sysprops-envvars.properties b/config/config-mp/src/test/resources/custom-mp-meta-config-sysprops-envvars.properties new file mode 100644 index 00000000000..d92c0050a30 --- /dev/null +++ b/config/config-mp/src/test/resources/custom-mp-meta-config-sysprops-envvars.properties @@ -0,0 +1,21 @@ +# +# Copyright (c) 2022 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. + + +add-discovered-sources=false +add-discovered-converters=false + +sources.0.type=system-properties +sources.1.type=environment-variables diff --git a/config/config-mp/src/test/resources/meta-config/classpath-dev.properties b/config/config-mp/src/test/resources/meta-config/classpath-dev.properties new file mode 100644 index 00000000000..b11a1901167 --- /dev/null +++ b/config/config-mp/src/test/resources/meta-config/classpath-dev.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022 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. +# + +value=classpath_profile diff --git a/config/config-mp/src/test/resources/meta-config/path-dev.properties b/config/config-mp/src/test/resources/meta-config/path-dev.properties new file mode 100644 index 00000000000..fa7fd3733ca --- /dev/null +++ b/config/config-mp/src/test/resources/meta-config/path-dev.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022 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. +# + +value=path_profile diff --git a/config/hocon-mp/etc/spotbugs/exclude.xml b/config/hocon-mp/etc/spotbugs/exclude.xml new file mode 100644 index 00000000000..5d6119e033c --- /dev/null +++ b/config/hocon-mp/etc/spotbugs/exclude.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff --git a/config/hocon-mp/pom.xml b/config/hocon-mp/pom.xml new file mode 100644 index 00000000000..04a4a753601 --- /dev/null +++ b/config/hocon-mp/pom.xml @@ -0,0 +1,70 @@ + + + + + 4.0.0 + + io.helidon.config + helidon-config-project + 2.5.1-SNAPSHOT + + helidon-config-hocon-mp + Helidon Config HOCON MP + + + HOCON support for Helidon MP. + + + + etc/spotbugs/exclude.xml + + + + + io.helidon.config + helidon-config-mp + + + com.typesafe + config + + + org.eclipse.microprofile.config + microprofile-config-api + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-core + test + + + + io.helidon.config + helidon-config-yaml-mp + test + + + diff --git a/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigIncluder.java b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigIncluder.java new file mode 100644 index 00000000000..efde0b25d11 --- /dev/null +++ b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigIncluder.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +import io.helidon.config.ConfigException; +import io.helidon.config.spi.ConfigParserException; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigIncludeContext; +import com.typesafe.config.ConfigIncluder; +import com.typesafe.config.ConfigObject; +import com.typesafe.config.ConfigParseOptions; + +import static java.lang.System.Logger.Level.TRACE; +import static java.lang.System.Logger.Level.WARNING; + +class HoconMpConfigIncluder implements ConfigIncluder { + private static final System.Logger LOGGER = System.getLogger(HoconMpConfigIncluder.class.getName()); + private static final String HOCON_EXTENSION = ".conf"; + + private ConfigParseOptions parseOptions; + private String relativeUrl; + private Path relativePath; + private Charset charset; + + HoconMpConfigIncluder() { + } + + @Override + public ConfigIncluder withFallback(ConfigIncluder fallback) { + return this; + } + + @Override + public ConfigObject include(ConfigIncludeContext context, String what) { + new Exception().printStackTrace(); + LOGGER.log(TRACE, String.format("Received request to include resource %s, %s", + what, context.parseOptions().getOriginDescription())); + + return relativeUrl != null ? parseHoconFromUrl(what) : parseHoconFromPath(what); + } + + private ConfigObject parseHoconFromUrl(String includeName) { + String includePath = relativeUrl + patchName(includeName); + URL includeUrl; + try { + includeUrl = new URL(includePath); + System.out.println("includeURL: " + includeUrl); + } catch (MalformedURLException e) { + LOGGER.log(WARNING, String.format("Unable to create include Url for: %s with error: %s", + includePath, e.getMessage())); + return ConfigFactory.empty().root(); + } + try (InputStreamReader readable = new InputStreamReader(includeUrl.openConnection().getInputStream(), charset)) { + Config typesafeConfig = ConfigFactory.parseReader(readable, parseOptions); + return typesafeConfig.root(); + } catch (IOException e) { + throw new ConfigParserException("Failed to read from include source: " + includeUrl, e); + } + } + + private ConfigObject parseHoconFromPath(String includeName) { + Path path = relativePath.resolve(includeName); + if (Files.exists(path) && Files.isReadable(path) && !Files.isDirectory(path)) { + System.out.println("Path: " + path); + try (BufferedReader reader = Files.newBufferedReader(path, charset)) { + Config typesafeConfig = ConfigFactory.parseReader(reader, parseOptions); + return typesafeConfig.root(); + } catch (IOException e) { + throw new ConfigException("Failed to read from include source: " + path.toAbsolutePath(), e); + } + } else { + return ConfigFactory.empty().root(); + } + } + + void charset(Charset charset) { + this.charset = charset; + } + + void parseOptions(ConfigParseOptions parseOptions) { + this.parseOptions = parseOptions; + } + + void relativeUrl(String relativeUrl) { + this.relativeUrl = relativeUrl; + } + + void relativePath(Path relativePath) { + this.relativePath = relativePath; + } + + /** + * Adds default Hocon extension if not present. + * + * @param what file name + * @return file name with extension + */ + private static String patchName(String what) { + Optional base = Optional.of(what) + .filter(f -> f.contains(File.separator)) + .map(f -> f.substring(f.lastIndexOf(File.separator) + 1)); + Optional ext = Optional.of(base.orElse(what)) + .filter(f -> f.contains(".")) + .map(f -> f.substring(f.lastIndexOf(".") + 1)); + return ext.isPresent() ? what : what + HOCON_EXTENSION; + } +} diff --git a/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigSource.java b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigSource.java new file mode 100644 index 00000000000..5de2eb647a5 --- /dev/null +++ b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpConfigSource.java @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import io.helidon.config.ConfigException; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigList; +import com.typesafe.config.ConfigObject; +import com.typesafe.config.ConfigParseOptions; +import com.typesafe.config.ConfigResolveOptions; +import com.typesafe.config.ConfigValue; +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * MicroProfile {@link org.eclipse.microprofile.config.spi.ConfigSource} that can be used + * to add HOCON/JSON files from classpath or file system using the + * {@link org.eclipse.microprofile.config.spi.ConfigProviderResolver#getBuilder()}. + *

The HOCON/JSON file is transformed to a flat map as follows:

+ * Object nodes + *

+ * Each node in the tree is dot separated. + *

+ * server = {
+ *   host = "localhost"
+ *   port= 8080
+ * }
+ * 
+ * Will be transformed to the following properties: + *
+ * server.host=localhost
+ * server.port=8080
+ * 
+ * List nodes (arrays) + *

+ * Each node will be indexed (0 based) + *

+ * providers =
+ *   [{abac = {enabled = true}}]
+ * names = [
+ *   first
+ *   second
+ *   third
+ * ]
+ * 
+ * Will be transformed to the following properties: + *
+ * providers.0.abac.enabled=true
+ * names.0=first
+ * names.1=second
+ * names.2=third
+ * 
+ */ +@SuppressWarnings("rawtypes") +public class HoconMpConfigSource implements ConfigSource { + private final String name; + private Map properties; + + private static final boolean RESOLVING_ENABLED = true; + private static final ConfigResolveOptions RESOLVE_OPTIONS = ConfigResolveOptions.defaults(); + + private HoconMpConfigSource(String name, Map properties) { + this.properties = properties; + this.name = "hocon: " + name; + } + + /** + * Load a HOCON/JSON config source from file system. + * + * @param path path to the HOCON/JSON file + * @return config source loaded from the file + * @see #create(java.net.URL) + */ + public static ConfigSource create(Path path) { + String name = path.toAbsolutePath().toString(); + + try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + ConfigParseOptions parseOptions = getConfigParseOptions(path); + + Config typesafeConfig = toConfig(reader, parseOptions); + // this is a mutable HashMap that we can use + Map props = + fromConfig(typesafeConfig.root().isEmpty() ? ConfigFactory.empty().root() : typesafeConfig.root()); + return new HoconMpConfigSource(name, props); + } catch (IOException e) { + throw new ConfigException("Failed to load HOCON/JSON config source from path: " + path.toAbsolutePath(), e); + } + } + + /** + * Load a HOCON/JSON config source from URL. + * The URL may be any URL which is supported by the used JVM. + * + * @param url url of the resource + * @return config source loaded from the URL + */ + public static ConfigSource create(URL url) { + try (InputStreamReader reader = new InputStreamReader(url.openConnection().getInputStream(), StandardCharsets.UTF_8)) { + String name = url.toString(); + ConfigParseOptions parseOptions = getConfigParseOptions(url); + Config typesafeConfig = toConfig(reader, parseOptions); + if (typesafeConfig.root().isEmpty()) { // empty source + return new HoconMpConfigSource(name, Map.of()); + } + return new HoconMpConfigSource(name, fromConfig(typesafeConfig.root())); + } catch (Exception e) { + throw new ConfigException("Failed to configure HOCON/JSON config source", e); + } + } + + private static ConfigParseOptions getConfigParseOptions(URL url) { + HoconMpConfigIncluder includer = new HoconMpConfigIncluder(); + ConfigParseOptions parseOptions = ConfigParseOptions.defaults().appendIncluder(includer); + includer.parseOptions(parseOptions); + includer.relativeUrl(getParentResourcePath(url.toString())); + includer.charset(StandardCharsets.UTF_8); + return parseOptions; + } + + private static ConfigParseOptions getConfigParseOptions(Path path) { + HoconMpConfigIncluder includer = new HoconMpConfigIncluder(); + ConfigParseOptions parseOptions = ConfigParseOptions.defaults().appendIncluder(includer); + includer.parseOptions(parseOptions); + includer.relativePath(path.getParent()); + includer.charset(StandardCharsets.UTF_8); + return parseOptions; + } + + private static Config toConfig(Reader content, ConfigParseOptions parseOptions) { + Config typesafeConfig = ConfigFactory.parseReader( + content, parseOptions); + if (RESOLVING_ENABLED) { + typesafeConfig = typesafeConfig.resolve(RESOLVE_OPTIONS); + } + return typesafeConfig; + } + + /** + * Create from HOCON/JSON file(s) on classpath. + * + * @param resource resource name to locate on classpath (looks for all instances) + * @return list of config sources discovered (may be zero length) + */ + public static List classPath(String resource) { + List sources = new LinkedList<>(); + try { + Thread.currentThread().getContextClassLoader().getResources(resource) + .asIterator() + .forEachRemaining(it -> sources.add(create(it))); + } catch (IOException e) { + throw new IllegalStateException("Failed to read HOCON/JSON \"" + resource + "\" from classpath", e); + } + + return sources; + } + + /** + * Create from HOCON/JSON file(s) on classpath with profile support. + * + * @param resource resource name to locate on classpath (looks for all instances) + * @param profile name of the profile to use + * @return list of config sources discovered (may be zero length) + */ + public static List classPath(String resource, String profile) { + Objects.requireNonNull(profile, "Profile must be defined"); + + List sources = new LinkedList<>(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + try { + Enumeration baseResources = classLoader.getResources(resource); + Enumeration profileResources = classLoader.getResources(toProfileResource(resource, profile)); + + if (profileResources.hasMoreElements()) { + List profileResourceList = new LinkedList<>(); + profileResources.asIterator() + .forEachRemaining(profileResourceList::add); + + baseResources.asIterator() + .forEachRemaining(it -> { + String pathBase = pathBase(it.toString()); + // we need to find profile that belongs to this + for (URL url : profileResourceList) { + String profilePathBase = pathBase(url.toString()); + if (pathBase.equals(profilePathBase)) { + // Main is the profile config file and fallback is the original config file + sources.add(create(create(url), create(it))); + } else { + sources.add(create(it)); + } + } + }); + } else { + baseResources + .asIterator() + .forEachRemaining(it -> sources.add(create(it))); + } + } catch (IOException e) { + throw new IllegalStateException("Failed to read HOCON/JSON \"" + resource + "\" from classpath", e); + } + + return sources; + } + + private static ConfigSource create(ConfigSource main, ConfigSource fallback) { + String name = main.getName() + " (" + fallback.getName() + ")"; + + return new ConfigSource() { + @Override + public Set getPropertyNames() { + Set result = new HashSet<>(fallback.getPropertyNames()); + result.addAll(main.getPropertyNames()); + + return result; + } + + @Override + public String getValue(String propertyName) { + String value = main.getValue(propertyName); + if (value == null) { + return fallback.getValue(propertyName); + } + return value; + } + + @Override + public String getName() { + return name; + } + + @Override + public Map getProperties() { + Map result = new HashMap<>(fallback.getProperties()); + result.putAll(main.getProperties()); + + return result; + } + }; + } + + private static String getParentResourcePath(String resource) { + // this works the same on windows and Unix systems (classpath is always forward slashes) + int lastSlash = resource.lastIndexOf('/'); + String rootOfResource; + if (lastSlash > -1) { + rootOfResource = resource.substring(0, lastSlash + 1); + } else { + rootOfResource = resource; + } + return rootOfResource; + } + + private static Map fromConfig(ConfigObject config) { + Map result = new HashMap<>(); + process(result, "", config); + + return result; + } + + private static void process(Map resultMap, String prefix, ConfigObject config) { + config.forEach((unescapedKey, value) -> { + String key = io.helidon.config.Config.Key.escapeName(unescapedKey); + processNext(resultMap, prefix(prefix, key), value); + }); + } + + private static void process(Map resultMap, String prefix, ConfigList configList) { + int counter = 0; + for (ConfigValue value : configList) { + processNext(resultMap, prefix(prefix, String.valueOf(counter)), value); + counter++; + } + } + + private static void processNext(Map resultMap, + String prefix, + ConfigValue value) { + if (value instanceof ConfigList) { + process(resultMap, prefix, (ConfigList) value); + } else if (value instanceof ConfigObject) { + process(resultMap, prefix, (ConfigObject) value); + } else { + Object unwrapped = value.unwrapped(); + String stringValue = (null == unwrapped) ? "" : String.valueOf(unwrapped); + resultMap.put(prefix, stringValue); + } + } + + private static String prefix(String prefix, String stringKey) { + if (prefix.isEmpty()) { + return stringKey; + } + + return prefix + "." + stringKey; + } + + private static String pathBase(String path) { + int i = path.lastIndexOf('/'); + int y = path.lastIndexOf('!'); + int z = path.lastIndexOf(':'); + int b = path.lastIndexOf('\\'); + + // we need the last index before the file name - so the highest number of all of the above + int max = Math.max(i, y); + max = Math.max(max, z); + max = Math.max(max, b); + + if (max > -1) { + return path.substring(0, max); + } + return path; + } + + private static String toProfileResource(String resource, String profile) { + int i = resource.lastIndexOf('.'); + if (i > -1) { + return resource.substring(0, i) + "-" + profile + resource.substring(i); + } + return resource + "-" + profile; + } + + @Override + public Set getPropertyNames() { + return Collections.unmodifiableSet(properties.keySet()); + } + + @Override + public Map getProperties() { + return Collections.unmodifiableMap(properties); + } + + @Override + public String getValue(String propertyName) { + return properties.get(propertyName); + } + + @Override + public String getName() { + return name; + } +} diff --git a/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpMetaConfigProvider.java b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpMetaConfigProvider.java new file mode 100644 index 00000000000..258ee448191 --- /dev/null +++ b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/HoconMpMetaConfigProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Prioritized; +import io.helidon.config.Config; +import io.helidon.config.mp.MpMetaConfigUtils; +import io.helidon.config.mp.spi.MpMetaConfigProvider; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Helidon MicroProfile meta-config provider for Hocon and Json. + */ +public class HoconMpMetaConfigProvider implements MpMetaConfigProvider, Prioritized { + @Override + public Set supportedTypes() { + return Set.of("hocon", "json"); + } + + @Override + public List create(String type, Config metaConfig, String profile) { + return MpMetaConfigUtils.sourceFromMeta( + metaConfig, + profile, + HoconMpConfigSource::create, + HoconMpConfigSource::classPath, + HoconMpConfigSource::classPath, + HoconMpConfigSource::create + ); + } + + @Override + public int priority() { + return 300; + } +} diff --git a/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/package-info.java b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/package-info.java new file mode 100644 index 00000000000..cc1a7e502d7 --- /dev/null +++ b/config/hocon-mp/src/main/java/io/helidon/config/hocon/mp/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 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. + */ + +/** + * HOCON config source for MicroProfile config. + * + * @see io.helidon.config Configuration API + */ +package io.helidon.config.hocon.mp; diff --git a/config/hocon-mp/src/main/java/module-info.java b/config/hocon-mp/src/main/java/module-info.java new file mode 100644 index 00000000000..df4f2dcea0c --- /dev/null +++ b/config/hocon-mp/src/main/java/module-info.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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. + */ + +/** + * Support for HOCON configuration sources. + */ +module io.helidon.config.hocon.mp { + requires microprofile.config.api; + + requires io.helidon.config.mp; + requires transitive io.helidon.config; + // requires io.helidon.config.hocon; + requires typesafe.config; + + exports io.helidon.config.hocon.mp; + + provides io.helidon.config.mp.spi.MpMetaConfigProvider with io.helidon.config.hocon.mp.HoconMpMetaConfigProvider; + +} diff --git a/config/hocon-mp/src/main/resources/META-INF/native-image/io.helidon.config/helidon-config-hocon-mp/native-image.properties b/config/hocon-mp/src/main/resources/META-INF/native-image/io.helidon.config/helidon-config-hocon-mp/native-image.properties new file mode 100644 index 00000000000..5347fb8a838 --- /dev/null +++ b/config/hocon-mp/src/main/resources/META-INF/native-image/io.helidon.config/helidon-config-hocon-mp/native-image.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022 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. +# + +Args=--initialize-at-build-time=io.helidon.config.hocon.mp diff --git a/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpConfigSourceTest.java b/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpConfigSourceTest.java new file mode 100644 index 00000000000..517dca09244 --- /dev/null +++ b/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpConfigSourceTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Enumeration; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +class HoconJsonMpConfigSourceTest { + @Test + void testHoconViaClasspath() throws IOException { + ConfigSource source = HoconMpConfigSource.create(getResourceUrlPath("application.conf")); + // Test Main Config + validateHoconConfig(source); + } + + @Test + void testHoconViaPath() { + ConfigSource source = HoconMpConfigSource.create(Paths.get("src/test/resources/application.conf")) ; + validateHoconConfig(source); + } + + private void validateHoconConfig(ConfigSource source) { + // Test Main Config + assertThat(source.getValue("hocon.string"), is("String")); + assertThat(source.getValue("hocon.number"), is("10")); + assertThat(source.getValue("hocon.array.0"), is("Array 1")); + assertThat(source.getValue("hocon.array.1"), is("Array 2")); + assertThat(source.getValue("hocon.array.2"), is("Array 3")); + assertThat(source.getValue("hocon.boolean"), is("true")); + // Test Include + assertThat(source.getValue("hocon_include.string"), is("Include String")); + assertThat(source.getValue("hocon_include.number"), is("20")); + assertThat(source.getValue("hocon_include.array.0"), is("Include Array 1")); + assertThat(source.getValue("hocon_include.array.1"), is("Include Array 2")); + assertThat(source.getValue("hocon_include.array.2"), is("Include Array 3")); + assertThat(source.getValue("hocon_include.boolean"), is("false")); + } + + @Test + void testJsonConfigViaClassPath() throws IOException { + ConfigSource source = HoconMpConfigSource.create(getResourceUrlPath("application.json")); + validateJsonConfig(source); + } + + @Test + void testJsonConfigViaPath() throws IOException { + ConfigSource source = HoconMpConfigSource.create(Paths.get("src/test/resources/application.json")); + validateJsonConfig(source); + } + + private void validateJsonConfig(ConfigSource source) { + assertThat(source.getValue("json.string"), is("String")); + assertThat(source.getValue("json.number"), is("10")); + assertThat(source.getValue("json.array.0"), is("Array 1")); + assertThat(source.getValue("json.array.1"), is("Array 2")); + assertThat(source.getValue("json.array.2"), is("Array 3")); + assertThat(source.getValue("json.boolean"), is("true")); + } + + private static URL getResourceUrlPath(String resource) throws IOException { + Enumeration resources = Thread.currentThread().getContextClassLoader().getResources(resource); + while (resources.hasMoreElements()) { + return resources.nextElement(); + } + return null; + } +} diff --git a/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java b/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java new file mode 100644 index 00000000000..b550e679551 --- /dev/null +++ b/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import io.helidon.config.ConfigException; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +class HoconJsonMpMetaConfigTest { + private static ConfigProviderResolver configResolver; + private Config config; + private static final String META_CONFIG_SYSTEM_PROPERTY = "io.helidon.config.mp.meta-config"; + + @BeforeAll + static void getProviderResolver() { + configResolver = ConfigProviderResolver.instance(); + } + @AfterAll + static void resetSystemProperties() { + System.clearProperty(META_CONFIG_SYSTEM_PROPERTY); + } + + @BeforeEach + void resetConfig() { + if (config == null) { + // first run - need to remove existing props + System.clearProperty(META_CONFIG_SYSTEM_PROPERTY); + configResolver.releaseConfig(ConfigProvider.getConfig()); + } else { + configResolver.releaseConfig(config); + config = null; + } + } + + @Test + void testMetaHoconClasspath() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-hocon-classpath.yaml"); + validateConfig(); + } + + @Test + void testMetaHoconPath() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-hocon-path.yaml"); + validateConfig(); + } + + private void validateConfig() { + config = ConfigProvider.getConfig(); + + // Main file + assertThat(config.getValue("string", String.class), is("String")); + assertThat(config.getValue("number", String.class), is("175")); + assertThat(config.getValue("array.0", String.class), is("One")); + assertThat(config.getValue("array.1", String.class), is("Two")); + assertThat(config.getValue("array.2", String.class), is("Three")); + assertThat(config.getValue("boolean", String.class), is("true")); + // Include file + assertThat(config.getValue("custom_include.string", String.class), is("Include_String")); + assertThat(config.getValue("custom_include.number", String.class), is("200")); + assertThat(config.getValue("custom_include.array.0", String.class), is("First")); + assertThat(config.getValue("custom_include.array.1", String.class), is("Second")); + assertThat(config.getValue("custom_include.array.2", String.class), is("Third")); + assertThat(config.getValue("custom_include.boolean", String.class), is("false")); + } + + @Test + void testMetaHoconClasspathConfigProfile() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-hocon-classpath-profile.yaml"); + validateConfigProfile(); + } + + @Test + void testMetaHoconPathConfigProfile() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-hocon-path-profile.yaml"); + validateConfigProfile(); + } + + private void validateConfigProfile() { + config = ConfigProvider.getConfig(); + + // Main file + assertThat(config.getValue("string", String.class), is("String_dev")); + assertThat(config.getValue("number", String.class), is("250")); + assertThat(config.getValue("array.0", String.class), is("One")); + assertThat(config.getValue("array.1", String.class), is("Two")); + assertThat(config.getValue("array.2", String.class), is("Three")); + assertThat(config.getValue("boolean", String.class), is("true")); + assertThat(config.getValue("extra", String.class), is("Extra")); + + // Include file + assertThat(config.getValue("custom_include.string", String.class), is("Include_String")); + assertThat(config.getValue("custom_include.number", String.class), is("200")); + assertThat(config.getValue("custom_include.array.0", String.class), is("Begin")); + assertThat(config.getValue("custom_include.array.1", String.class), is("Middle")); + assertThat(config.getValue("custom_include.array.2", String.class), is("End")); + assertThat(config.getValue("custom_include.boolean", String.class), is("true")); + } + + + @Test + void testMetaJson() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-json.yaml"); + config = ConfigProvider.getConfig(); + + assertThat(config.getValue("string", String.class), is("String")); + assertThat(config.getValue("number", String.class), is("175")); + assertThat(config.getValue("array.0", String.class), is("One")); + assertThat(config.getValue("array.1", String.class), is("Two")); + assertThat(config.getValue("array.2", String.class), is("Three")); + assertThat(config.getValue("boolean", String.class), is("true")); + } + + @Test + void testMetaOrdinal() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "ordinal-mp-meta-config-hocon.yaml"); + config = ConfigProvider.getConfig(); + + assertThat(config.getValue("string", String.class), is("String")); + assertThat(config.getValue("number", String.class), is("175")); + assertThat(config.getValue("array.0", String.class), is("First")); + assertThat(config.getValue("array.1", String.class), is("Second")); + assertThat(config.getValue("array.2", String.class), is("Third")); + assertThat(config.getValue("boolean", String.class), is("false")); + } + + @Test + void testMetaHoconNonExistentNotOptional() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-hocon-path-not-optional.yaml"); + try { + config = ConfigProvider.getConfig(); + Assertions.fail("Expecting meta-config to fail due to not optional non-existent config source"); + } catch (ConfigException e) {} + } +} diff --git a/config/hocon-mp/src/test/resources/application.conf b/config/hocon-mp/src/test/resources/application.conf new file mode 100644 index 00000000000..1ca37739357 --- /dev/null +++ b/config/hocon-mp/src/test/resources/application.conf @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022 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. +# + +include "include.conf" + +hocon = { + string = "String" + number = 10 + array = [ + "Array 1" + "Array 2" + "Array 3" + ] + boolean = true +} diff --git a/config/hocon-mp/src/test/resources/application.json b/config/hocon-mp/src/test/resources/application.json new file mode 100644 index 00000000000..5979a65e88b --- /dev/null +++ b/config/hocon-mp/src/test/resources/application.json @@ -0,0 +1,12 @@ +{ + "json": { + "string": "String", + "number": 10, + "array": [ + "Array 1", + "Array 2", + "Array 3" + ], + "boolean": true + } +} diff --git a/config/hocon-mp/src/test/resources/custom-application-dev.conf b/config/hocon-mp/src/test/resources/custom-application-dev.conf new file mode 100644 index 00000000000..5b06273d36d --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-application-dev.conf @@ -0,0 +1,21 @@ +# +# Copyright (c) 2022 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. +# +include "custom-include-dev.conf" + +string = String_dev +number = 250 +extra = Extra + diff --git a/config/hocon-mp/src/test/resources/custom-application.conf b/config/hocon-mp/src/test/resources/custom-application.conf new file mode 100644 index 00000000000..09eca799183 --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-application.conf @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 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. +# +include "custom-include.conf" + +string = String +number = 175 +array = [ + One + Two + Three +] +boolean = true diff --git a/config/hocon-mp/src/test/resources/custom-application.json b/config/hocon-mp/src/test/resources/custom-application.json new file mode 100644 index 00000000000..08cb127271e --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-application.json @@ -0,0 +1,10 @@ +{ + "string": "String", + "number": 175, + "array": [ + "One", + "Two", + "Three" + ], + "boolean": true +} diff --git a/config/hocon-mp/src/test/resources/custom-include-dev.conf b/config/hocon-mp/src/test/resources/custom-include-dev.conf new file mode 100644 index 00000000000..98a220a45e1 --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-include-dev.conf @@ -0,0 +1,24 @@ +# +# Copyright (c) 2022 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. +# + +custom_include { + array = [ + Begin + Middle + End + ] + boolean = true +} diff --git a/config/hocon-mp/src/test/resources/custom-include.conf b/config/hocon-mp/src/test/resources/custom-include.conf new file mode 100644 index 00000000000..c75b064276e --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-include.conf @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 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. +# + +custom_include { + string = Include_String + number = 200 + array = [ + First + Second + Third + ] + boolean = false +} diff --git a/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath-profile.yaml b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath-profile.yaml new file mode 100644 index 00000000000..7d114fa309c --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath-profile.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false +profile: dev + +sources: + - type: "hocon" + classpath: "custom-application.conf" + optional: false diff --git a/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath.yaml b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath.yaml new file mode 100644 index 00000000000..362529a681b --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-classpath.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "hocon" + classpath: "custom-application.conf" + optional: false diff --git a/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-not-optional.yaml b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-not-optional.yaml new file mode 100644 index 00000000000..2d5994fb98a --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-not-optional.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "hocon" + path: "src/test/resources/non-existent.conf" + optional: false diff --git a/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-profile.yaml b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-profile.yaml new file mode 100644 index 00000000000..2ee86556fe4 --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path-profile.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false +profile: dev + +sources: + - type: "hocon" + path: "src/test/resources/custom-application.conf" + optional: false diff --git a/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path.yaml b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path.yaml new file mode 100644 index 00000000000..41a09ac3c89 --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-mp-meta-config-hocon-path.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "hocon" + path: "src/test/resources/custom-application.conf" + optional: false diff --git a/config/hocon-mp/src/test/resources/custom-mp-meta-config-json.yaml b/config/hocon-mp/src/test/resources/custom-mp-meta-config-json.yaml new file mode 100644 index 00000000000..c34c2315f1b --- /dev/null +++ b/config/hocon-mp/src/test/resources/custom-mp-meta-config-json.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "json" + classpath: "custom-application.json" + optional: false diff --git a/config/hocon-mp/src/test/resources/include.conf b/config/hocon-mp/src/test/resources/include.conf new file mode 100644 index 00000000000..32d5814b1ee --- /dev/null +++ b/config/hocon-mp/src/test/resources/include.conf @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 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. +# + +hocon_include { + string = "Include String" + number = 20 + array = [ + "Include Array 1" + "Include Array 2" + "Include Array 3" + ] + boolean = false +} diff --git a/config/hocon-mp/src/test/resources/ordinal-mp-meta-config-hocon.yaml b/config/hocon-mp/src/test/resources/ordinal-mp-meta-config-hocon.yaml new file mode 100644 index 00000000000..250cd984d82 --- /dev/null +++ b/config/hocon-mp/src/test/resources/ordinal-mp-meta-config-hocon.yaml @@ -0,0 +1,27 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "json" + classpath: "ordinal.json" + optional: false + ordinal: 500 + - type: "hocon" + classpath: "ordinal.conf" + optional: false diff --git a/config/hocon-mp/src/test/resources/ordinal.conf b/config/hocon-mp/src/test/resources/ordinal.conf new file mode 100644 index 00000000000..09eca799183 --- /dev/null +++ b/config/hocon-mp/src/test/resources/ordinal.conf @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 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. +# +include "custom-include.conf" + +string = String +number = 175 +array = [ + One + Two + Three +] +boolean = true diff --git a/config/hocon-mp/src/test/resources/ordinal.json b/config/hocon-mp/src/test/resources/ordinal.json new file mode 100644 index 00000000000..f8dcee08fd3 --- /dev/null +++ b/config/hocon-mp/src/test/resources/ordinal.json @@ -0,0 +1,8 @@ +{ + "array": [ + "First", + "Second", + "Third" + ], + "boolean": false +} diff --git a/config/pom.xml b/config/pom.xml index 08b7810202d..41a5baa3e26 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -50,6 +50,7 @@ yaml-mp metadata metadata-processor + hocon-mp diff --git a/config/yaml-mp/pom.xml b/config/yaml-mp/pom.xml index b9e5d1cfb1a..282307e994b 100644 --- a/config/yaml-mp/pom.xml +++ b/config/yaml-mp/pom.xml @@ -38,6 +38,10 @@ + + io.helidon.config + helidon-config-mp + io.helidon.config helidon-config-yaml diff --git a/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMetaConfigProvider.java b/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMetaConfigProvider.java new file mode 100644 index 00000000000..88d876464e2 --- /dev/null +++ b/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMetaConfigProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 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.yaml.mp; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Prioritized; +import io.helidon.config.Config; +import io.helidon.config.mp.MpMetaConfigUtils; +import io.helidon.config.mp.spi.MpMetaConfigProvider; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** + * Helidon MicroProfile meta-config provider for Yaml. + */ +public class YamlMetaConfigProvider implements MpMetaConfigProvider, Prioritized { + @Override + public Set supportedTypes() { + return Set.of("yaml"); + } + + @Override + public List create(String type, Config metaConfig, String profile) { + return MpMetaConfigUtils.sourceFromMeta( + metaConfig, + profile, + YamlMpConfigSource::create, + YamlMpConfigSource::classPath, + YamlMpConfigSource::classPath, + YamlMpConfigSource::create + ); + } + + @Override + public int priority() { + return 300; + } +} diff --git a/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMpConfigSource.java b/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMpConfigSource.java index ac01428c46f..3e1d60c8f29 100644 --- a/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMpConfigSource.java +++ b/config/yaml-mp/src/main/java/io/helidon/config/yaml/mp/YamlMpConfigSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 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. @@ -217,7 +217,8 @@ public static List classPath(String resource, String profile) { for (URL url : profileResourceList) { String profilePathBase = pathBase(url.toString()); if (pathBase.equals(profilePathBase)) { - sources.add(create(create(it), create(url))); + // Main is the profile config file and fallback is the original config file + sources.add(create(create(url), create(it))); } else { sources.add(create(it)); } diff --git a/config/yaml-mp/src/main/java/module-info.java b/config/yaml-mp/src/main/java/module-info.java index 7017a0af9ba..b397f5d9575 100644 --- a/config/yaml-mp/src/main/java/module-info.java +++ b/config/yaml-mp/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 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. @@ -21,10 +21,12 @@ requires org.yaml.snakeyaml; requires microprofile.config.api; + requires io.helidon.config.mp; requires transitive io.helidon.config; requires io.helidon.config.yaml; exports io.helidon.config.yaml.mp; provides org.eclipse.microprofile.config.spi.ConfigSourceProvider with io.helidon.config.yaml.mp.YamlConfigSourceProvider; -} \ No newline at end of file + provides io.helidon.config.mp.spi.MpMetaConfigProvider with io.helidon.config.yaml.mp.YamlMetaConfigProvider; +} diff --git a/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpConfigSourceTest.java b/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpConfigSourceTest.java index 7210b009597..99d2b7c40c4 100644 --- a/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpConfigSourceTest.java +++ b/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpConfigSourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 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. @@ -16,7 +16,11 @@ package io.helidon.config.yaml.mp; +import java.io.IOException; import java.io.StringReader; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Enumeration; import org.eclipse.microprofile.config.spi.ConfigSource; import org.junit.jupiter.api.Test; @@ -48,4 +52,33 @@ void testListNode() { assertThat(source.getValue("names.1"), is("second")); assertThat(source.getValue("names.2"), is("third")); } + + @Test + void testConfigViaClassPath() throws IOException { + ConfigSource source = YamlMpConfigSource.create(getResourceUrlPath("application.yaml")); + validateConfig(source); + } + + @Test + void testConfigViaPath() throws IOException { + ConfigSource source = YamlMpConfigSource.create(Paths.get("src/test/resources/application.yaml")); + validateConfig(source); + } + + private void validateConfig(ConfigSource source) { + assertThat(source.getValue("yaml.string"), is("String")); + assertThat(source.getValue("yaml.number"), is("10")); + assertThat(source.getValue("yaml.array.0"), is("Array 1")); + assertThat(source.getValue("yaml.array.1"), is("Array 2")); + assertThat(source.getValue("yaml.array.2"), is("Array 3")); + assertThat(source.getValue("yaml.boolean"), is("true")); + } + + private static URL getResourceUrlPath(String resource) throws IOException { + Enumeration resources = Thread.currentThread().getContextClassLoader().getResources(resource); + while (resources.hasMoreElements()) { + return resources.nextElement(); + } + return null; + } } diff --git a/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpMetaConfigTest.java b/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpMetaConfigTest.java new file mode 100644 index 00000000000..97f887ab6da --- /dev/null +++ b/config/yaml-mp/src/test/java/io/helidon/config/yaml/mp/YamlMpMetaConfigTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2022 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.yaml.mp; + +import io.helidon.config.ConfigException; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Enumeration; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +class YamlMpMetaConfigTest { + private static ConfigProviderResolver configResolver; + private Config config; + private static final String META_CONFIG_SYSTEM_PROPERTY = "io.helidon.config.mp.meta-config"; + + @BeforeAll + static void getProviderResolver() { + configResolver = ConfigProviderResolver.instance(); + } + @AfterAll + static void resetSystemProperties() { + System.clearProperty(META_CONFIG_SYSTEM_PROPERTY); + } + + @BeforeEach + void resetConfig() { + if (config == null) { + // first run - need to remove existing props + System.clearProperty(META_CONFIG_SYSTEM_PROPERTY); + configResolver.releaseConfig(ConfigProvider.getConfig()); + } else { + configResolver.releaseConfig(config); + config = null; + } + } + + @Test + void testMetaClasspath() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-classpath.yaml"); + validateConfig(); + } + + @Test + void testMetaPath() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-path.yaml"); + validateConfig(); + } + + private void validateConfig() { + config = ConfigProvider.getConfig(); + + // Main file + assertThat(config.getValue("string", String.class), is("String")); + assertThat(config.getValue("number", String.class), is("175")); + assertThat(config.getValue("array.0", String.class), is("One")); + assertThat(config.getValue("array.1", String.class), is("Two")); + assertThat(config.getValue("array.2", String.class), is("Three")); + assertThat(config.getValue("boolean", String.class), is("true")); + } + + @Test + void testMetaClasspathConfigProfile() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-classpath-profile.yaml"); + validateConfigProfile(); + } + + @Test + void testMetaPathConfigProfile() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-path-profile.yaml"); + validateConfigProfile(); + } + + private void validateConfigProfile() { + config = ConfigProvider.getConfig(); + + // Main file + assertThat(config.getValue("string", String.class), is("String_dev")); + assertThat(config.getValue("number", String.class), is("250")); + assertThat(config.getValue("array.0", String.class), is("One")); + assertThat(config.getValue("array.1", String.class), is("Two")); + assertThat(config.getValue("array.2", String.class), is("Three")); + assertThat(config.getValue("boolean", String.class), is("true")); + assertThat(config.getValue("extra", String.class), is("Extra")); + } + + @Test + void testMetaOrdinal() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "ordinal-mp-meta-config.yaml"); + config = ConfigProvider.getConfig(); + + assertThat(config.getValue("string", String.class), is("String")); + assertThat(config.getValue("number", String.class), is("175")); + assertThat(config.getValue("array.0", String.class), is("First")); + assertThat(config.getValue("array.1", String.class), is("Second")); + assertThat(config.getValue("array.2", String.class), is("Third")); + assertThat(config.getValue("boolean", String.class), is("false")); + } + + @Test + void testMetaNonExistentNotOptional() { + System.setProperty(META_CONFIG_SYSTEM_PROPERTY, "custom-mp-meta-config-path-not-optional.yaml"); + try { + config = ConfigProvider.getConfig(); + Assertions.fail("Expecting meta-config to fail due to not optional non-existent config source"); + } catch (ConfigException e) {} + } +} diff --git a/config/yaml-mp/src/test/resources/application.yaml b/config/yaml-mp/src/test/resources/application.yaml new file mode 100644 index 00000000000..dcf23a34e72 --- /dev/null +++ b/config/yaml-mp/src/test/resources/application.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 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. +# + +--- +yaml: + string: String + number: 10 + array: + - "Array 1" + - "Array 2" + - "Array 3" + boolean: true diff --git a/config/yaml-mp/src/test/resources/custom-application-dev.yaml b/config/yaml-mp/src/test/resources/custom-application-dev.yaml new file mode 100644 index 00000000000..5dad87ff294 --- /dev/null +++ b/config/yaml-mp/src/test/resources/custom-application-dev.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2022 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. +# + +--- +string: String_dev +number: 250 +extra: Extra diff --git a/config/yaml-mp/src/test/resources/custom-application.yaml b/config/yaml-mp/src/test/resources/custom-application.yaml new file mode 100644 index 00000000000..d4e37625be3 --- /dev/null +++ b/config/yaml-mp/src/test/resources/custom-application.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2022 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. +# + +--- +string: String +number: 175 +array: + - One + - Two + - Three +boolean: true diff --git a/config/config-mp/src/test/resources/custom-mp-meta-config.yaml b/config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath-profile.yaml similarity index 60% rename from config/config-mp/src/test/resources/custom-mp-meta-config.yaml rename to config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath-profile.yaml index 745e2d161c4..4c6bc4d69f0 100644 --- a/config/config-mp/src/test/resources/custom-mp-meta-config.yaml +++ b/config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath-profile.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021 Oracle and/or its affiliates. +# Copyright (c) 2022 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. @@ -16,20 +16,9 @@ add-discovered-sources: false add-discovered-converters: false -#future compatibility -#config-profile: "dev" +profile: dev sources: - - type: "properties" - path: "src/test/resources/meta-config/path.properties" - ordinal: 50 - type: "yaml" classpath: "custom-application.yaml" - optional: true - - type: "properties" - classpath: "meta-config/classpath.properties" - name: "CLASSPATH" - # not sure how to test URL based unless we start a server -# - type: "yaml" -# url: "helidon-test:meta-config/url.yaml" -# name: "URL" \ No newline at end of file + optional: false diff --git a/config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath.yaml b/config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath.yaml new file mode 100644 index 00000000000..ce5d5680029 --- /dev/null +++ b/config/yaml-mp/src/test/resources/custom-mp-meta-config-classpath.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "yaml" + classpath: "custom-application.yaml" + optional: false diff --git a/config/yaml-mp/src/test/resources/custom-mp-meta-config-path-not-optional.yaml b/config/yaml-mp/src/test/resources/custom-mp-meta-config-path-not-optional.yaml new file mode 100644 index 00000000000..f029cc67198 --- /dev/null +++ b/config/yaml-mp/src/test/resources/custom-mp-meta-config-path-not-optional.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "yaml" + path: "src/test/resources/non-existent.conf" + optional: false diff --git a/config/yaml-mp/src/test/resources/custom-mp-meta-config-path-profile.yaml b/config/yaml-mp/src/test/resources/custom-mp-meta-config-path-profile.yaml new file mode 100644 index 00000000000..f2297b8cb57 --- /dev/null +++ b/config/yaml-mp/src/test/resources/custom-mp-meta-config-path-profile.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false +profile: dev + +sources: + - type: "yaml" + path: "src/test/resources/custom-application.yaml" + optional: false diff --git a/config/yaml-mp/src/test/resources/custom-mp-meta-config-path.yaml b/config/yaml-mp/src/test/resources/custom-mp-meta-config-path.yaml new file mode 100644 index 00000000000..0f2c84bf7ee --- /dev/null +++ b/config/yaml-mp/src/test/resources/custom-mp-meta-config-path.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "yaml" + path: "src/test/resources/custom-application.yaml" + optional: false diff --git a/config/yaml-mp/src/test/resources/ordinal-2.yaml b/config/yaml-mp/src/test/resources/ordinal-2.yaml new file mode 100644 index 00000000000..d4e37625be3 --- /dev/null +++ b/config/yaml-mp/src/test/resources/ordinal-2.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2022 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. +# + +--- +string: String +number: 175 +array: + - One + - Two + - Three +boolean: true diff --git a/config/yaml-mp/src/test/resources/ordinal-mp-meta-config.yaml b/config/yaml-mp/src/test/resources/ordinal-mp-meta-config.yaml new file mode 100644 index 00000000000..2b81e740e8b --- /dev/null +++ b/config/yaml-mp/src/test/resources/ordinal-mp-meta-config.yaml @@ -0,0 +1,27 @@ +# +# Copyright (c) 2022 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. +# + +add-discovered-sources: false +add-discovered-converters: false + +sources: + - type: "yaml" + classpath: "ordinal.yaml" + optional: false + ordinal: 500 + - type: "yaml" + classpath: "ordinal-2.yaml" + optional: false diff --git a/config/yaml-mp/src/test/resources/ordinal.yaml b/config/yaml-mp/src/test/resources/ordinal.yaml new file mode 100644 index 00000000000..07074b86990 --- /dev/null +++ b/config/yaml-mp/src/test/resources/ordinal.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2022 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. +# + +--- +array: +- First +- Second +- Third +boolean: false diff --git a/examples/metrics/exemplar/pom.xml b/examples/metrics/exemplar/pom.xml index 2cbdfd9892c..36e5e6543e5 100644 --- a/examples/metrics/exemplar/pom.xml +++ b/examples/metrics/exemplar/pom.xml @@ -40,6 +40,10 @@ io.helidon.webserver helidon-webserver + + io.helidon.config + helidon-config-yaml + io.helidon.media helidon-media-jsonp diff --git a/examples/metrics/filtering/se/pom.xml b/examples/metrics/filtering/se/pom.xml index 7cd23c35f98..945eb09797f 100644 --- a/examples/metrics/filtering/se/pom.xml +++ b/examples/metrics/filtering/se/pom.xml @@ -38,6 +38,10 @@ io.helidon.webserver helidon-webserver + + io.helidon.config + helidon-config-yaml + io.helidon.media helidon-media-jsonp diff --git a/examples/metrics/kpi/pom.xml b/examples/metrics/kpi/pom.xml index 1d391f2fbd5..98e6fa77355 100644 --- a/examples/metrics/kpi/pom.xml +++ b/examples/metrics/kpi/pom.xml @@ -38,6 +38,10 @@ io.helidon.webserver helidon-webserver + + io.helidon.config + helidon-config-yaml + io.helidon.media helidon-media-jsonp diff --git a/microprofile/cdi/pom.xml b/microprofile/cdi/pom.xml index f8fa2105a72..10480639a71 100644 --- a/microprofile/cdi/pom.xml +++ b/microprofile/cdi/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + io.helidon.tests.integration + helidon-tests-integration-config + 2.5.1-SNAPSHOT + + + helidon-tests-integration-config-hocon-mp + Helidon Tests Integration for HOCON/JSON Config on MP + Validation for HOCON/JSON Config on MP + + + + io.helidon.config + helidon-config-hocon-mp + test + + + io.helidon.microprofile.server + helidon-microprofile-server + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-core + test + + + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test + + + diff --git a/tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/ConfigBean.java b/tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/ConfigBean.java new file mode 100644 index 00000000000..ab75b3b3a2e --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/ConfigBean.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import javax.inject.Inject; + +public class ConfigBean { + // Main Hocon config file properties + @Inject + @ConfigProperty(name = "hocon.string") + public String hocon_string; + + @Inject + @ConfigProperty(name = "hocon.number") + public int hocon_number; + + @Inject + @ConfigProperty(name = "hocon.array.0") + public String hocon_array_0; + + @Inject + @ConfigProperty(name = "hocon.array.1") + public String hocon_array_1; + + @Inject + @ConfigProperty(name = "hocon.array.2") + public String hocon_array_2; + + @Inject + @ConfigProperty(name = "hocon.boolean") + public boolean hocon_boolean; + + // Include properties + @Inject + @ConfigProperty(name = "hocon_include.string") + public String hocon_include_string; + + @Inject + @ConfigProperty(name = "hocon_include.number") + public int hocon_include_number; + + @Inject + @ConfigProperty(name = "hocon_include.array.0") + public String hocon_include_array_0; + + @Inject + @ConfigProperty(name = "hocon_include.array.1") + public String hocon_include_array_1; + + @Inject + @ConfigProperty(name = "hocon_include.array.2") + public String hocon_include_array_2; + + @Inject + @ConfigProperty(name = "hocon_include.boolean") + public boolean hocon_include_boolean; + + // Json config file properties + @Inject + @ConfigProperty(name = "json.string") + public String json_string; + + @Inject + @ConfigProperty(name = "json.number") + public int json_number; + + @Inject + @ConfigProperty(name = "json.array.0") + public String json_array_0; + + @Inject + @ConfigProperty(name = "json.array.1") + public String json_array_1; + + @Inject + @ConfigProperty(name = "json.array.2") + public String json_array_2; + + @Inject + @ConfigProperty(name = "json.boolean") + public boolean json_boolean; +} diff --git a/tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java b/tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java new file mode 100644 index 00000000000..1c133901c4f --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/java/io/helidon/config/hocon/mp/HoconJsonMpMetaConfigTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 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.hocon.mp; + +import io.helidon.microprofile.config.ConfigCdiExtension; +import io.helidon.microprofile.server.JaxRsCdiExtension; +import io.helidon.microprofile.server.ServerCdiExtension; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import javax.enterprise.inject.se.SeContainer; +import javax.enterprise.inject.se.SeContainerInitializer; +import javax.enterprise.inject.spi.CDI; + +/** + * Test meta-config on MP with HOCON and JSON parsing. + * + * This will use SeContainerInitializer rather than @HelidonTest as the latter does not make meta-config work + */ +public class HoconJsonMpMetaConfigTest { + private static ConfigBean bean; + private static SeContainer container; + + @BeforeAll + static void initialize() { + System.setProperty("io.helidon.config.mp.meta-config", "custom-mp-meta-config.yaml"); + System.setProperty("mp.initializer.allow", "true"); + System.setProperty("mp.initializer.no-warn", "true"); + + container = SeContainerInitializer.newInstance() + .disableDiscovery() + .addExtensions(ConfigCdiExtension.class, ServerCdiExtension.class, JaxRsCdiExtension.class) + .addBeanClasses(ConfigBean.class) + .initialize(); + + bean = CDI.current() + .select(ConfigBean.class) + .get(); + } + + @AfterAll + static void destroy() { + System.clearProperty("io.helidon.config.mp.meta-config"); + System.clearProperty("mp.initializer.allow"); + System.clearProperty("mp.initializer.no-warn"); + container.close(); + } + + @Test + void TestHoconConfig() { + Assertions.assertEquals(bean.hocon_string, "Meta String"); + Assertions.assertEquals(bean.hocon_number, 20); + Assertions.assertEquals(bean.hocon_array_0, "Meta Array 1"); + Assertions.assertEquals(bean.hocon_array_1, "Meta Array 2"); + Assertions.assertEquals(bean.hocon_array_2, "Meta Array 3"); + Assertions.assertTrue(bean.hocon_boolean); + } + + @Test + void TestHoconIncludeConfig() { + Assertions.assertEquals(bean.hocon_include_string, "Meta Include String"); + Assertions.assertEquals(bean.hocon_include_number, 10); + Assertions.assertEquals(bean.hocon_include_array_0, "Meta Include Array 1"); + Assertions.assertEquals(bean.hocon_include_array_1, "Meta Include Array 2"); + Assertions.assertEquals(bean.hocon_include_array_2, "Meta Include Array 3"); + Assertions.assertFalse(bean.hocon_include_boolean); + } + + @Test + void TestJsonConfig() { + Assertions.assertEquals(bean.json_string, "Meta String"); + Assertions.assertEquals(bean.json_number, 20); + Assertions.assertEquals(bean.json_array_0, "Meta Array 1"); + Assertions.assertEquals(bean.json_array_1, "Meta Array 2"); + Assertions.assertEquals(bean.json_array_2, "Meta Array 3"); + Assertions.assertTrue(bean.json_boolean); + } +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/application-dev.conf b/tests/integration/config/hocon-mp/src/test/resources/application-dev.conf new file mode 100644 index 00000000000..75f578a2ca7 --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/application-dev.conf @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 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. +# + +include "include-dev.conf" + +hocon = { + string = "Overriden String" + array = [ + "Overriden Array 1" + "Overriden Array 2" + "Overriden Array 3" + ] +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/application-dev.json b/tests/integration/config/hocon-mp/src/test/resources/application-dev.json new file mode 100644 index 00000000000..d29ef2d5138 --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/application-dev.json @@ -0,0 +1,10 @@ +{ + "json": { + "string": "Overriden String", + "array": [ + "Overriden Array 1", + "Overriden Array 2", + "Overriden Array 3" + ] + } +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/application.conf b/tests/integration/config/hocon-mp/src/test/resources/application.conf new file mode 100644 index 00000000000..1ca37739357 --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/application.conf @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022 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. +# + +include "include.conf" + +hocon = { + string = "String" + number = 10 + array = [ + "Array 1" + "Array 2" + "Array 3" + ] + boolean = true +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/application.json b/tests/integration/config/hocon-mp/src/test/resources/application.json new file mode 100644 index 00000000000..5979a65e88b --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/application.json @@ -0,0 +1,12 @@ +{ + "json": { + "string": "String", + "number": 10, + "array": [ + "Array 1", + "Array 2", + "Array 3" + ], + "boolean": true + } +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/custom-mp-meta-config.yaml b/tests/integration/config/hocon-mp/src/test/resources/custom-mp-meta-config.yaml new file mode 100644 index 00000000000..74ad4a8de37 --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/custom-mp-meta-config.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 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: "hocon" + classpath: "meta-application.conf" + optional: false + - type: "json" + classpath: "meta-application.json" + optional: false diff --git a/tests/integration/config/hocon-mp/src/test/resources/include-dev.conf b/tests/integration/config/hocon-mp/src/test/resources/include-dev.conf new file mode 100644 index 00000000000..32d5814b1ee --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/include-dev.conf @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 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. +# + +hocon_include { + string = "Include String" + number = 20 + array = [ + "Include Array 1" + "Include Array 2" + "Include Array 3" + ] + boolean = false +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/include.conf b/tests/integration/config/hocon-mp/src/test/resources/include.conf new file mode 100644 index 00000000000..32d5814b1ee --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/include.conf @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 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. +# + +hocon_include { + string = "Include String" + number = 20 + array = [ + "Include Array 1" + "Include Array 2" + "Include Array 3" + ] + boolean = false +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/meta-application.conf b/tests/integration/config/hocon-mp/src/test/resources/meta-application.conf new file mode 100644 index 00000000000..fa8887cf167 --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/meta-application.conf @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022 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. +# + +include "meta-include.conf" + +hocon = { + string = "Meta String" + number = 20 + array = [ + "Meta Array 1" + "Meta Array 2" + "Meta Array 3" + ] + boolean = true +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/meta-application.json b/tests/integration/config/hocon-mp/src/test/resources/meta-application.json new file mode 100644 index 00000000000..5b63c1a109a --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/meta-application.json @@ -0,0 +1,12 @@ +{ + "json": { + "string": "Meta String", + "number": 20, + "array": [ + "Meta Array 1", + "Meta Array 2", + "Meta Array 3" + ], + "boolean": true + } +} diff --git a/tests/integration/config/hocon-mp/src/test/resources/meta-include.conf b/tests/integration/config/hocon-mp/src/test/resources/meta-include.conf new file mode 100644 index 00000000000..c7859215156 --- /dev/null +++ b/tests/integration/config/hocon-mp/src/test/resources/meta-include.conf @@ -0,0 +1,26 @@ +# +# Copyright (c) 2022 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. +# + +hocon_include { + string = "Meta Include String" + number = 10 + array = [ + "Meta Include Array 1" + "Meta Include Array 2" + "Meta Include Array 3" + ] + boolean = false +} diff --git a/tests/integration/config/pom.xml b/tests/integration/config/pom.xml index 9d94061f88d..b232c291f8e 100644 --- a/tests/integration/config/pom.xml +++ b/tests/integration/config/pom.xml @@ -37,5 +37,6 @@ gh-2171 gh-2171-yml + hocon-mp - \ No newline at end of file +