Skip to content

Commit

Permalink
4.x: @HelidonTest / @addconfig* Provide a config parser by type #8718 (
Browse files Browse the repository at this point in the history
…#8721)

4.x: @HelidonTest / @addconfig* Provide a config parser by type #8718

Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
  • Loading branch information
jbescos committed May 7, 2024
1 parent 33a78af commit 9fb58b9
Show file tree
Hide file tree
Showing 17 changed files with 226 additions and 106 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
* Copyright (c) 2020, 2024 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.
Expand Down Expand Up @@ -47,7 +47,6 @@
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;
Expand Down Expand Up @@ -84,30 +83,6 @@ class MpConfigBuilder implements Builder<MpConfigBuilder, Config>, ConfigBuilder
private static final System.Logger LOGGER = System.getLogger(MpConfigBuilder.class.getName());
private static final String DEFAULT_CONFIG_SOURCE = "META-INF/microprofile-config.properties";

private static final Map<String, MpMetaConfigProvider> MP_META_PROVIDERS;
static {
List<MpMetaConfigProvider> mpMetaConfigProviders =
HelidonServiceLoader.builder(ServiceLoader.load(MpMetaConfigProvider.class))
.addService(new MpEnvironmentVariablesMetaConfigProvider())
.addService(new MpSystemPropertiesMetaConfigProvider())
.addService(new MpPropertiesMetaConfigProvider())
.build()
.asList();

// Helidon service loader uses Weight and Weighted by default, we want to use priorities here
Priorities.sort(mpMetaConfigProviders, Prioritized.DEFAULT_PRIORITY);

Map<String, MpMetaConfigProvider> 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<OrdinalSource> sources = new LinkedList<>();
private final List<OrdinalConverter> converters = new LinkedList<>();

Expand Down Expand Up @@ -253,10 +228,10 @@ private void processMetaSources(List<io.helidon.config.Config> 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"));
MpMetaConfigProvider mpMetaConfigProvider = MP_META_PROVIDERS.get(type);
MpMetaConfigProvider mpMetaConfigProvider = MpConfigSources.MP_META_PROVIDERS.get(type);
if (mpMetaConfigProvider == null) {
throw new ConfigException("Wrong meta configuration, type " + type
+ " not supported, only supporting: " + MP_META_PROVIDERS.keySet());
+ " not supported, only supporting: " + MpConfigSources.MP_META_PROVIDERS.keySet());
}

List<? extends ConfigSource> delegates = mpMetaConfigProvider.create(type, config, profile);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates.
* Copyright (c) 2020, 2024 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.
Expand All @@ -18,8 +18,11 @@

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
Expand All @@ -31,11 +34,14 @@
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;

import io.helidon.common.HelidonServiceLoader;
import io.helidon.config.Config;
import io.helidon.config.ConfigException;
import io.helidon.config.MutabilitySupport;
import io.helidon.config.mp.spi.MpMetaConfigProvider;

import org.eclipse.microprofile.config.spi.ConfigSource;

Expand All @@ -62,6 +68,31 @@
* </ul>
*/
public final class MpConfigSources {

static final Map<String, MpMetaConfigProvider> MP_META_PROVIDERS;

static {
List<MpMetaConfigProvider> mpMetaConfigProviders =
HelidonServiceLoader.builder(ServiceLoader.load(MpMetaConfigProvider.class))
.addService(new MpEnvironmentVariablesMetaConfigProvider())
.addService(new MpSystemPropertiesMetaConfigProvider())
.addService(new MpPropertiesMetaConfigProvider())
.build()
.asList();

// Helidon service loader uses Weight and Weighted by default, we want to use priorities here
Priorities.sort(mpMetaConfigProviders, Prioritized.DEFAULT_PRIORITY);

Map<String, MpMetaConfigProvider> 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 MpConfigSources() {
}

Expand Down Expand Up @@ -355,6 +386,50 @@ public static ConfigSource create(Config config) {
return new MpHelidonConfigSource(Objects.requireNonNull(config, "Config cannot be null"));
}

/**
* Config source base on a Reader instance for the given type.
* This method will not close the reader.
*
* @param type of the given reader (properties, YAML, etc).
* @param reader with the configuration.
* @return a new MicroProfile config source
*/
public static ConfigSource create(String type, Reader reader) {
Objects.requireNonNull(type, "Type cannot be null");
Objects.requireNonNull(reader, "Reader cannot be null");
MpMetaConfigProvider provider = MP_META_PROVIDERS.get(type);
if (provider == null) {
throw new IllegalArgumentException("There is no MpMetaConfigProvider registered for type " + type
+ ". Make sure the desired provider exists in modulepath/classpath.");
}
return provider.create(reader);
}

/**
* Config source base on a URL for the given type.
* This method will not close the reader.
*
* @param type of the given URL (properties, YAML, etc).
* @param url with resource.
* @return a new MicroProfile config source
*/
public static ConfigSource create(String type, URL url) {
Objects.requireNonNull(type, "Type cannot be null");
Objects.requireNonNull(url, "URL cannot be null");
MpMetaConfigProvider provider = MP_META_PROVIDERS.get(type);

if (provider == null) {
throw new IllegalArgumentException("There is no MpMetaConfigProvider registered for type " + type
+ " in " + url + ". Make sure the desired provider exists in modulepath/classpath.");
}

try (InputStreamReader reader = new InputStreamReader(url.openConnection().getInputStream(), StandardCharsets.UTF_8)) {
return provider.create(reader);
} catch (Exception e) {
throw new ConfigException("Failed to configure " + type + " config source", e);
}
}

/**
* Create a composite config source that uses the main first, and if it does not find
* a property in main, uses fallback. This is useful to set up a config source with a profile,
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates.
* Copyright (c) 2020, 2024 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.
Expand Down Expand Up @@ -42,4 +42,5 @@ public List<? extends ConfigSource> create(String type, Config metaConfig, Strin
public int priority() {
return 300;
}

}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 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.
Expand All @@ -16,7 +16,11 @@

package io.helidon.config.mp;

import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import io.helidon.config.Config;
Expand Down Expand Up @@ -49,4 +53,16 @@ public List<? extends ConfigSource> create(String type, Config metaConfig, Strin
public int priority() {
return 300;
}

@Override
public ConfigSource create(Reader content) {
Properties p = new Properties();
try {
p.load(content);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return MpConfigSources.create("MpPropertiesMetaConfigProvider", p);
}

}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 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.
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 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.
Expand All @@ -16,6 +16,7 @@

package io.helidon.config.mp.spi;

import java.io.Reader;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -44,4 +45,15 @@ public interface MpMetaConfigProvider {
* @return list of config sources
*/
List<? extends ConfigSource> create(String type, Config metaConfig, String profile);

/**
* Create the {@link ConfigSource} from the given content.
*
* @param content a reader with the content data
* @return config source
*/
default ConfigSource create(Reader content) {
throw new UnsupportedOperationException("Create method is not supported by "
+ getClass().getName());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates.
* Copyright (c) 2020, 2024 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.
Expand All @@ -17,6 +17,7 @@
package io.helidon.config.mp;

import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -108,6 +109,21 @@ void testMpConfigSourcesNullConfig() {
assertThat(npe.getMessage(), is("Config cannot be null"));
}

@Test
void testPropertiesMetaConfigProvider() {
typeChecks("properties", """
another1.key=another1.value
another2.key=another2.value
""");
}

private void typeChecks(String type, String content) {
org.eclipse.microprofile.config.spi.ConfigSource source =
MpConfigSources.create(type, new StringReader(content));
assertThat(source.getValue("another1.key"), is("another1.value"));
assertThat(source.getValue("another2.key"), is("another2.value"));
}

private static final class NodeImpl implements ConfigSource, NodeConfigSource {
private static final String DESCRIPTION = "node-unit-test";
private static final String KEY = "key";
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 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.
Expand Down Expand Up @@ -109,17 +109,28 @@ public static ConfigSource create(Path path) {

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<String, String> props =
fromConfig(typesafeConfig.root().isEmpty() ? ConfigFactory.empty().root() : typesafeConfig.root());
return new HoconMpConfigSource(name, props);
return create(name, reader, parseOptions);
} 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 a reader.
*
* @param name the name of the config
* @param reader that will read the configuration
* @param parseOptions of the content
* @return config source loaded
*/
public static ConfigSource create(String name, Reader reader, ConfigParseOptions parseOptions) {
Config typesafeConfig = toConfig(reader, parseOptions);
// this is a mutable HashMap that we can use
Map<String, String> props =
fromConfig(typesafeConfig.root().isEmpty() ? ConfigFactory.empty().root() : typesafeConfig.root());
return new HoconMpConfigSource(name, props);
}

/**
* Load a HOCON/JSON config source from URL.
* The URL may be any URL which is supported by the used JVM.
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 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.
Expand All @@ -16,6 +16,7 @@

package io.helidon.config.hocon.mp;

import java.io.Reader;
import java.util.List;
import java.util.Set;

Expand All @@ -24,6 +25,7 @@
import io.helidon.config.mp.Prioritized;
import io.helidon.config.mp.spi.MpMetaConfigProvider;

import com.typesafe.config.ConfigParseOptions;
import org.eclipse.microprofile.config.spi.ConfigSource;

/**
Expand Down Expand Up @@ -51,4 +53,10 @@ public List<? extends ConfigSource> create(String type, Config metaConfig, Strin
public int priority() {
return 300;
}

@Override
public ConfigSource create(Reader content) {
return HoconMpConfigSource.create("HoconMpMetaConfigProvider", content,
ConfigParseOptions.defaults());
}
}

0 comments on commit 9fb58b9

Please sign in to comment.