Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Hocon/Json Configuration Source for MP #4347

Merged
merged 3 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@
<artifactId>helidon-config-yaml-mp</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-hocon-mp</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-git</artifactId>
Expand Down
4 changes: 0 additions & 4 deletions config/config-mp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml-mp</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down
217 changes: 30 additions & 187 deletions config/config-mp/src/main/java/io/helidon/config/mp/MpConfigBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -47,15 +46,14 @@
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;
import java.util.ServiceLoader;
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;
Expand All @@ -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;
Expand All @@ -83,6 +81,27 @@ 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<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();

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 @@ -153,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;
Expand Down Expand Up @@ -236,25 +247,14 @@ 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"));
// in MP, we have a hardcoded list of supported configuration source types
List<ConfigSource> 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<? extends ConfigSource> delegates = mpMetaConfigProvider.create(type, config, profile);

boolean shouldCount = delegates.size() > 1;
int counter = 0;

Expand All @@ -278,163 +278,6 @@ private void processMetaSources(List<io.helidon.config.Config> configs) {
}
}

private List<ConfigSource> propertiesSource(io.helidon.config.Config config) {
return sourceFromMeta(config,
MpConfigSources::create,
MpConfigSources::classPath,
MpConfigSources::classPath,
MpConfigSources::create);
}

private List<ConfigSource> yamlSource(io.helidon.config.Config config) {
return sourceFromMeta(config,
YamlMpConfigSource::create,
YamlMpConfigSource::classPath,
YamlMpConfigSource::classPath,
YamlMpConfigSource::create);
}

private List<ConfigSource> sourceFromMeta(io.helidon.config.Config config,
Function<Path, ConfigSource> fromPath,
Function<String, List<ConfigSource>> fromClasspath,
BiFunction<String, String, List<ConfigSource>> fromClasspathWithProfile,
Function<URL, ConfigSource> fromUrl) {

boolean optional = config.get("optional").asBoolean().orElse(false);

String location;
Exception cause = null;

ConfigValue<Path> pathConfig = config.get("path").as(Path.class);
if (pathConfig.isPresent()) {
Path path = pathConfig.get();
List<ConfigSource> result = sourceFromPathMeta(path, fromPath);

if (!result.isEmpty()) {
return result;
}
// else the file was not found, check optional
location = "path " + path.toAbsolutePath();
} else {
ConfigValue<String> classpathConfig = config.get("classpath").as(String.class);
if (classpathConfig.isPresent()) {
String classpath = classpathConfig.get();
List<ConfigSource> sources;

if (profile == null) {
sources = fromClasspath.apply(classpath);
} else {
sources = fromClasspathWithProfile.apply(classpath, profile);
}

if (!sources.isEmpty()) {
return sources;
}
location = "classpath " + classpath;
} else {
ConfigValue<URL> urlConfig = config.get("url").as(URL.class);
if (urlConfig.isPresent()) {
URL url = urlConfig.get();
List<ConfigSource> 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<ConfigSource> sourceFromUrlMeta(URL url, Function<URL, ConfigSource> 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<ConfigSource> sourceFromPathMeta(Path path, Function<Path, ConfigSource> 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<ConfigSource> 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> supportedTypes() {
return Set.of("environment-variables");
}

@Override
public List<? extends ConfigSource> create(String type, Config metaConfig, String profile) {
return List.of(MpConfigSources.environmentVariables());
}

@Override
public int priority() {
return 300;
}
}
Loading