Skip to content

Commit

Permalink
Load Profile Sources before Factories so the profile values are avail…
Browse files Browse the repository at this point in the history
…able in the Factory context (#771)
  • Loading branch information
radcortez authored Jun 30, 2022
1 parent 9a90db5 commit bf945f5
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.OptionalInt;
import java.util.function.Consumer;

import org.eclipse.microprofile.config.spi.ConfigSource;
Expand Down Expand Up @@ -188,14 +189,22 @@ protected List<ConfigSource> tryHttpResource(final URI uri, final int ordinal) {

protected List<ConfigSource> tryProfiles(final URI uri, final ConfigSource mainSource) {
final List<ConfigSource> configSources = new ArrayList<>();
configSources.add(new ConfigurableConfigSource((ProfileConfigSourceFactory) profiles -> {
final List<ConfigSource> profileSources = new ArrayList<>();
for (int i = profiles.size() - 1; i >= 0; i--) {
final int ordinal = mainSource.getOrdinal() + profiles.size() - i;
final URI profileUri = addProfileName(uri, profiles.get(i));
addProfileConfigSource(toURL(profileUri), ordinal, profileSources);
configSources.add(new ConfigurableConfigSource(new ProfileConfigSourceFactory() {
@Override
public Iterable<ConfigSource> getProfileConfigSources(final List<String> profiles) {
final List<ConfigSource> profileSources = new ArrayList<>();
for (int i = profiles.size() - 1; i >= 0; i--) {
final int ordinal = mainSource.getOrdinal() + profiles.size() - i;
final URI profileUri = addProfileName(uri, profiles.get(i));
AbstractLocationConfigSourceLoader.this.addProfileConfigSource(toURL(profileUri), ordinal, profileSources);
}
return profileSources;
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(mainSource.getOrdinal());
}
return profileSources;
}));
return configSources;
}
Expand Down Expand Up @@ -316,18 +325,4 @@ private static URI decodeIfNeeded(final URI uri) {
return uri;
}
}

interface ProfileConfigSourceFactory extends ConfigSourceFactory {
@Override
default Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
final List<String> profiles = context.getProfiles();
if (profiles.isEmpty()) {
return Collections.emptyList();
}

return getProfileConfigSources(profiles);
}

Iterable<ConfigSource> getProfileConfigSources(final List<String> profiles);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,7 @@ ConfigSourceFactory getFactory() {
}

List<ConfigSource> getConfigSources(final ConfigSourceContext context) {
return unwrap(context, new ArrayList<>());
}

private List<ConfigSource> unwrap(final ConfigSourceContext context, final List<ConfigSource> configSources) {
final List<ConfigSource> configSources = new ArrayList<>();
for (final ConfigSource configSource : factory.getConfigSources(context)) {
if (configSource instanceof ConfigurableConfigSource) {
configSources.addAll(((ConfigurableConfigSource) configSource).getConfigSources(context));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.smallrye.config;

import java.util.Collections;
import java.util.List;

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

interface ProfileConfigSourceFactory extends ConfigSourceFactory {
@Override
default Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
final List<String> profiles = context.getProfiles();
if (profiles.isEmpty()) {
return Collections.emptyList();
}

return getProfileConfigSources(profiles);
}

Iterable<ConfigSource> getProfileConfigSources(final List<String> profiles);
}
100 changes: 58 additions & 42 deletions implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -538,15 +538,14 @@ private static class ConfigSources implements Serializable {

// Init all late sources
List<String> profiles = getProfiles(interceptors);
List<ConfigSourceWithPriority> sourcesWithPriorities = mapLateSources(current, sources, profiles,
builder.isAddDiscoveredSources());
List<ConfigSourceWithPriority> sourcesWithPriorities = mapLateSources(sources, interceptors, current, profiles,
builder);

// Rebuild the chain with the late sources and new instances of the interceptors
// The new instance will ensure that we get rid of references to factories and other stuff and keep only
// the resolved final source or interceptor to use.
current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(sourcesWithPriorities),
current);
current = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(sourcesWithPriorities), current);
for (ConfigSourceInterceptor interceptor : interceptors) {
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current);
}
Expand Down Expand Up @@ -612,73 +611,90 @@ private static List<String> getProfiles(final List<ConfigSourceInterceptor> inte
}

private static List<ConfigSourceWithPriority> mapLateSources(
final SmallRyeConfigSourceInterceptorContext initChain,
final List<ConfigSource> sources,
final List<ConfigSourceInterceptor> interceptors,
final ConfigSourceInterceptorContext current,
final List<String> profiles,
final boolean addDiscoveredSources) {
final SmallRyeConfigBuilder builder) {

ConfigSourceWithPriority.resetLoadPriority();

List<ConfigurableConfigSource> lateSources = new ArrayList<>();
for (ConfigSource source : sources) {
if (source instanceof ConfigurableConfigSource) {
lateSources.add((ConfigurableConfigSource) source);
List<ConfigSourceWithPriority> currentSources = new ArrayList<>();

// Init all profile sources first
List<ConfigSource> profileSources = new ArrayList<>();
ConfigSourceContext mainContext = new SmallRyeConfigSourceContext(current, profiles);
for (ConfigurableConfigSource profileSource : getConfigurableSources(sources)) {
if (profileSource.getFactory() instanceof ProfileConfigSourceFactory) {
profileSources.addAll(profileSource.getConfigSources(mainContext));
}
}
lateSources.sort(Comparator.comparingInt(ConfigurableConfigSource::getOrdinal));

int countSourcesFromLocations = 0;
List<ConfigSourceWithPriority> sourcesWithPriority = new ArrayList<>();
for (ConfigurableConfigSource configurableSource : lateSources) {
final List<ConfigSource> configSources = configurableSource.getConfigSources(new ConfigSourceContext() {
@Override
public ConfigValue getValue(final String name) {
ConfigValue value = initChain.proceed(name);
return value != null ? value : ConfigValue.builder().withName(name).build();
}
// Sort the profiles sources with the main sources
currentSources.addAll(mapSources(profileSources));
currentSources.addAll(mapSources(sources));
currentSources.sort(null);
Collections.reverse(currentSources);

@Override
public List<String> getProfiles() {
return profiles;
}
// Rebuild the chain with the profiles sources, so profiles values are also available in factories
ConfigSourceInterceptorContext context = new SmallRyeConfigSourceInterceptorContext(EMPTY, null);
context = new SmallRyeConfigSourceInterceptorContext(new SmallRyeConfigSources(currentSources), context);
for (ConfigSourceInterceptor interceptor : interceptors) {
context = new SmallRyeConfigSourceInterceptorContext(interceptor, context);
}

@Override
public Iterator<String> iterateNames() {
return initChain.iterateNames();
// Init remaining sources, coming from SmallRyeConfig
int countSourcesFromLocations = 0;
List<ConfigSource> lateSources = new ArrayList<>();
ConfigSourceContext profileContext = new SmallRyeConfigSourceContext(context, profiles);
for (ConfigurableConfigSource lateSource : getConfigurableSources(sources)) {
if (!(lateSource.getFactory() instanceof ProfileConfigSourceFactory)) {
List<ConfigSource> configSources = lateSource.getConfigSources(profileContext);

if (lateSource.getFactory() instanceof AbstractLocationConfigSourceFactory) {
countSourcesFromLocations = countSourcesFromLocations + configSources.size();
}
});

if (configurableSource.getFactory() instanceof AbstractLocationConfigSourceFactory) {
countSourcesFromLocations = countSourcesFromLocations + configSources.size();
}

for (ConfigSource configSource : configSources) {
sourcesWithPriority.add(new ConfigSourceWithPriority(configSource));
lateSources.addAll(configSources);
}
}

if (countSourcesFromLocations == 0 && addDiscoveredSources) {
ConfigValue locations = initChain.proceed(SMALLRYE_CONFIG_LOCATIONS);
if (countSourcesFromLocations == 0 && builder.isAddDiscoveredSources()) {
ConfigValue locations = profileContext.getValue(SMALLRYE_CONFIG_LOCATIONS);
if (locations != null && locations.getValue() != null) {
ConfigLogging.log.configLocationsNotFound(SMALLRYE_CONFIG_LOCATIONS, locations.getValue());
}
}

sourcesWithPriority.addAll(mapSources(sources));
sourcesWithPriority.sort(null);
Collections.reverse(sourcesWithPriority);
// Sort the final sources
currentSources.clear();
currentSources.addAll(mapSources(lateSources));
currentSources.addAll(mapSources(profileSources));
currentSources.addAll(mapSources(sources));
currentSources.sort(null);
Collections.reverse(currentSources);

return sourcesWithPriority;
return currentSources;
}

private static List<ConfigSource> getSources(final List<ConfigSourceWithPriority> sourceWithPriorities) {
final List<ConfigSource> configSources = new ArrayList<>();
List<ConfigSource> configSources = new ArrayList<>();
for (ConfigSourceWithPriority configSourceWithPriority : sourceWithPriorities) {
configSources.add(configSourceWithPriority.getSource());
}
return Collections.unmodifiableList(configSources);
}

private static List<ConfigurableConfigSource> getConfigurableSources(final List<ConfigSource> sources) {
List<ConfigurableConfigSource> configurableConfigSources = new ArrayList<>();
for (ConfigSource source : sources) {
if (source instanceof ConfigurableConfigSource) {
configurableConfigSources.add((ConfigurableConfigSource) source);
}
}
configurableConfigSources.sort(Comparator.comparingInt(ConfigurableConfigSource::getOrdinal).reversed());
return Collections.unmodifiableList(configurableConfigSources);
}

/**
* Generate dotted properties from Env properties.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.smallrye.config;

import java.util.Iterator;
import java.util.List;

class SmallRyeConfigSourceContext implements ConfigSourceContext {
private final ConfigSourceInterceptorContext context;
private final List<String> profiles;

public SmallRyeConfigSourceContext(final ConfigSourceInterceptorContext context, final List<String> profiles) {
this.context = context;
this.profiles = profiles;
}

@Override
public ConfigValue getValue(final String name) {
ConfigValue value = context.proceed(name);
return value != null ? value : ConfigValue.builder().withName(name).build();
}

@Override
public List<String> getProfiles() {
return profiles;
}

@Override
public Iterator<String> iterateNames() {
return context.iterateNames();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,28 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;

import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;

import com.sun.net.httpserver.HttpServer;

import io.smallrye.config.common.MapBackedConfigSource;
import io.smallrye.testing.logging.LogCapture;

class PropertiesLocationConfigSourceFactoryTest {
Expand Down Expand Up @@ -281,6 +288,67 @@ void warningConfigLocationsNotFoundFromExisting() {
assertTrue(logCapture.records().isEmpty());
}

@Test
void profileSourcesInContext(@TempDir Path tempDir) throws Exception {
Properties mainProperties = new Properties();
mainProperties.setProperty("config_ordinal", "150");
mainProperties.setProperty("my.prop.main", "main");
try (FileOutputStream out = new FileOutputStream(tempDir.resolve("config.properties").toFile())) {
mainProperties.store(out, null);
}

Properties devProperties = new Properties();
devProperties.setProperty("my.prop.dev", "dev");
try (FileOutputStream out = new FileOutputStream(tempDir.resolve("config-dev.properties").toFile())) {
devProperties.store(out, null);
}

SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultSources()
.addDiscoveredSources()
.addDefaultInterceptors()
.withProfile("dev")
.withSources(new ConfigSourceProvider() {
@Override
public Iterable<ConfigSource> getConfigSources(final ClassLoader forClassLoader) {
AbstractLocationConfigSourceLoader configSourceLoader = new AbstractLocationConfigSourceLoader() {
@Override
protected String[] getFileExtensions() {
return new String[] { "properties" };
}

@Override
protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
return new PropertiesConfigSource(url, ordinal);
}
};

return configSourceLoader.loadConfigSources(tempDir.resolve("config.properties").toUri().toString(),
250);
}
})
.withSources(new ConfigSourceFactory() {
@Override
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
Map<String, String> values = new HashMap<>();
values.put("context.main", context.getValue("my.prop.main").getRawValue());
values.put("context.dev", context.getValue("my.prop.dev").getRawValue());
return Collections.singletonList(new MapBackedConfigSource("map", values) {
});
}
})
.build();

assertEquals("main", config.getRawValue("my.prop.main"));
assertEquals("dev", config.getRawValue("my.prop.dev"));
assertEquals("main", config.getRawValue("context.main"));
assertEquals("dev", config.getRawValue("context.dev"));

for (final ConfigSource configSource : config.getConfigSources()) {
System.out.println("configSource = " + configSource);
}
}

private static SmallRyeConfig buildConfig(String... locations) {
return new SmallRyeConfigBuilder()
.addDiscoveredSources()
Expand Down

0 comments on commit bf945f5

Please sign in to comment.