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

Initial work to mix sources and interceptors priorities. #313

Merged
merged 3 commits into from
Jun 16, 2020
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
9 changes: 5 additions & 4 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

* xref:config/config.adoc[Config]

* xref:config-sources/config-sources.adoc[Config Sources]

* xref:converters/converters.adoc[Converters]

* xref:interceptors/interceptors.adoc[Interceptors]

* Extensions
** xref:config-sources/config-sources.adoc[Config Sources]
** xref:converters/converters.adoc[Converters]
** xref:cdi/cdi.adoc[CDI]
* xref:cdi/cdi.adoc[CDI]
22 changes: 22 additions & 0 deletions doc/modules/ROOT/pages/config-sources/config-sources.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ include::../attributes.adoc[]

= Config Sources

SmallRye Config provides a few enhancements and extensions to MicroProfile Config `ConfigSource`.

== Config Source Factory

Another way to create a `ConfigSource` is via the `ConfigSourceFactory`. The difference between the SmallRye Factory
and the standard way to create a `ConfigSource` as specified in MicroProfile Config, is the Factory ability to provide
a context with access to the available configuration.

By implementing https://github.com/smallrye/smallrye-config/blob/master/implementation/src/main/java/io/smallrye/config/ConfigSourceFactoryjava[ConfigSourceFactory], the ConfigSource is made available via the
`ConfigSource getSource(ConfigSourceContext context)` method. The `ConfigSourceFactory` can be also assigned a priority,
like any other `ConfigSource` by overriding the method `OptionalInt getPriority()`.

When the Factory is initializing, a `ConfigSourceContext` is provided, that allows to call a single method
`ConfigValue getValue(String name)`. This method lookups configurations names in all `ConfigSources` that are already
initialized by the `Config`. These are the `ConfigSources` with higher priority than the Factory. With the
`ConfigSourceFactory` it is possible to bootstrap a `ConfigSource` that configures itself with other `ConfigSources`.

Registration of a `ConfigSourceFactory` is done via the `ServiceLoader` mechanism by providing the
implementation classes in a `META-INF/services/io.smallrye.config.ConfigSourceFactory` file. Alternatively, factories
may be registered via the Programmatic API in `SmallRyeConfigBuilder#withSources`.

== Available Config Sources
In addition to the default Config Sources specified by MicroProfile Config (Environment Variables, System Properties
and `microprofile-config.properties` file), SmallRye Config provides the following additional Sources:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.smallrye.config;

import io.smallrye.common.annotation.Experimental;

/**
* Exposes contextual information on the ConfigSource initialization via {@link ConfigSourceFactory}.
*/
@Experimental("ConfigSource API Enhancements")
public interface ConfigSourceContext {
ConfigValue getValue(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.smallrye.config;

import java.util.OptionalInt;

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

import io.smallrye.common.annotation.Experimental;

/**
* This ConfigSourceFactory allows to initialize a {@link ConfigSource}, with access to the current
* {@link ConfigSourceContext}.
* <p>
*
* The provided {@link ConfigSource} is initialized in priority order and the current {@link ConfigSourceContext} has
* access to all previous initialized {@code ConfigSources}. This allows the factory to configure the
* {@link ConfigSource} with configurations read from higher priority {@code ConfigSources}.
* <p>
*
* Instances of this interface will be discovered by {@link SmallRyeConfigBuilder#withSources(ConfigSourceFactory...)}
* via the {@link java.util.ServiceLoader} mechanism and can be registered by providing a
* {@code META-INF/services/io.smallrye.config.ConfigSourceFactory} which contains the fully qualified class name of the
* custom {@link ConfigSourceFactory} implementation.
*/
@Experimental("ConfigSource API Enhancements")
public interface ConfigSourceFactory {
ConfigSource getSource(ConfigSourceContext context);

default OptionalInt getPriority() {
return OptionalInt.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
* initialized.
* <p>
*
* Instances of this interface will be discovered by {@code SmallRyeConfigBuilder#addDiscoveredInterceptors()} via the
* Instances of this interface will be discovered by {@link SmallRyeConfigBuilder#addDiscoveredInterceptors()} via the
* {@link java.util.ServiceLoader} mechanism and can be registered by providing a
* {@code META-INF/services/io.smallrye.config.ConfigSourceInterceptorFactory}
* {@linkplain ClassLoader#getResource(String) resource} which contains the fully qualified class name of the
* custom {@code ConfigSourceProvider} implementation.
* {@code META-INF/services/io.smallrye.config.ConfigSourceInterceptorFactory} which contains the fully qualified class
* name of the custom {@link ConfigSourceInterceptor} implementation.
*/
@Experimental("Interceptor API to intercept resolution of a configuration name")
public interface ConfigSourceInterceptorFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public int getOrdinal() {
return configSource.getOrdinal();
}

ConfigSource unwrap() {
return configSource;
}

static ConfigValueConfigSource wrap(final ConfigSource configSource) {
if (configSource instanceof ConfigValueConfigSource) {
return (ConfigValueConfigSource) configSource;
Expand Down
130 changes: 83 additions & 47 deletions implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static io.smallrye.config.ConfigSourceInterceptor.EMPTY;
import static io.smallrye.config.SmallRyeConfigSourceInterceptor.configSourceInterceptor;
import static java.util.stream.Collectors.toList;

import java.io.Serializable;
import java.lang.reflect.Type;
Expand Down Expand Up @@ -48,20 +49,8 @@
* @author <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2017 Red Hat inc.
*/
public class SmallRyeConfig implements Config, Serializable {

private static final long serialVersionUID = 8138651532357898263L;

static final Comparator<ConfigSource> CONFIG_SOURCE_COMPARATOR = new Comparator<ConfigSource>() {
@Override
public int compare(ConfigSource o1, ConfigSource o2) {
int res = Integer.compare(o2.getOrdinal(), o1.getOrdinal());
// if 2 config sources have the same ordinal,
// provide consistent order by sorting them
// according to their name.
return res != 0 ? res : o2.getName().compareTo(o1.getName());
}
};

private final AtomicReference<ConfigSources> configSources;
private final Map<Type, Converter<?>> converters;
private final Map<Type, Converter<Optional<?>>> optionalConverters = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -229,14 +218,7 @@ public Iterable<ConfigSource> getConfigSources() {
*/
@Deprecated
public void addConfigSource(ConfigSource configSource) {
configSources.updateAndGet(configSources -> {
List<ConfigSource> currentSources = configSources.getSources();

int oldSize = currentSources.size();
List<ConfigSource> newSources = Arrays.asList(currentSources.toArray(new ConfigSource[oldSize + 1]));
newSources.set(oldSize, configSource);
return new ConfigSources(newSources, configSources);
});
configSources.updateAndGet(configSources -> new ConfigSources(configSources, configSource));
}

public <T> T convert(String value, Class<T> asType) {
Expand Down Expand Up @@ -270,7 +252,7 @@ private static class ConfigSources implements Serializable {
private static final long serialVersionUID = 3483018375584151712L;

private final List<ConfigSource> sources;
private final List<ConfigSourceInterceptor> interceptors;
private final List<ConfigSourceInterceptorWithPriority> interceptors;
private final ConfigSourceInterceptorContext interceptorChain;

/**
Expand All @@ -285,23 +267,21 @@ private static class ConfigSources implements Serializable {
* @param interceptors the Interceptors to be part of Config.
*/
ConfigSources(final List<ConfigSource> sources, final List<InterceptorWithPriority> interceptors) {
sources.sort(CONFIG_SOURCE_COMPARATOR);
interceptors.sort(Comparator.comparingInt(InterceptorWithPriority::getPriority).reversed());
final List<ConfigSourceInterceptorWithPriority> sortInterceptors = new ArrayList<>();
sortInterceptors.addAll(sources.stream().map(ConfigSourceInterceptorWithPriority::new).collect(toList()));
sortInterceptors.addAll(interceptors.stream().map(ConfigSourceInterceptorWithPriority::new).collect(toList()));
sortInterceptors.sort(Comparator.comparingInt(ConfigSourceInterceptorWithPriority::getPriority));

List<ConfigSourceInterceptor> initializedInterceptors = new ArrayList<>();
final List<ConfigSourceInterceptorWithPriority> initInterceptors = new ArrayList<>();
SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null);
for (int i = sources.size() - 1; i >= 0; i--) {
current = new SmallRyeConfigSourceInterceptorContext(configSourceInterceptor(sources.get(i)), current);
}

for (int i = interceptors.size() - 1; i >= 0; i--) {
ConfigSourceInterceptor interceptor = interceptors.get(i).getInterceptor(current);
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current);
initializedInterceptors.add(interceptor);
for (ConfigSourceInterceptorWithPriority configSourceInterceptor : sortInterceptors) {
final ConfigSourceInterceptorWithPriority initInterceptor = configSourceInterceptor.getInterceptor(current);
current = new SmallRyeConfigSourceInterceptorContext(initInterceptor.getInterceptor(), current);
initInterceptors.add(initInterceptor);
}

this.sources = Collections.unmodifiableList(sources);
this.interceptors = Collections.unmodifiableList(initializedInterceptors);
this.sources = Collections.unmodifiableList(getSources(initInterceptors));
this.interceptors = Collections.unmodifiableList(initInterceptors);
this.interceptorChain = current;
}

Expand All @@ -311,36 +291,92 @@ private static class ConfigSources implements Serializable {
* Config Sources are added to the Config.
*
* @param sources the Config Sources to be part of Config.
* @param configSources the previous ConfigSources
* @param configSource the new ConfigSource to add into the interceptor the chain.
*/
ConfigSources(final List<ConfigSource> sources, final ConfigSources configSources) {
sources.sort(CONFIG_SOURCE_COMPARATOR);
ConfigSources(final ConfigSources sources, final ConfigSource configSource) {
final int oldSize = sources.getInterceptors().size();
final List<ConfigSourceInterceptorWithPriority> newInterceptors = Arrays
.asList(sources.getInterceptors().toArray(new ConfigSourceInterceptorWithPriority[oldSize + 1]));
newInterceptors.set(oldSize, new ConfigSourceInterceptorWithPriority(configSource));
newInterceptors.sort(Comparator.comparingInt(ConfigSourceInterceptorWithPriority::getPriority));

SmallRyeConfigSourceInterceptorContext current = new SmallRyeConfigSourceInterceptorContext(EMPTY, null);

for (int i = sources.size() - 1; i >= 0; i--) {
current = new SmallRyeConfigSourceInterceptorContext(configSourceInterceptor(sources.get(i)), current);
}

for (int i = configSources.getInterceptors().size() - 1; i >= 0; i--) {
current = new SmallRyeConfigSourceInterceptorContext(configSources.getInterceptors().get(i), current);
for (ConfigSourceInterceptorWithPriority configSourceInterceptor : newInterceptors) {
final ConfigSourceInterceptor interceptor = configSourceInterceptor.getInterceptor();
current = new SmallRyeConfigSourceInterceptorContext(interceptor, current);
}

this.sources = Collections.unmodifiableList(sources);
this.interceptors = configSources.getInterceptors();
this.sources = Collections.unmodifiableList(getSources(newInterceptors));
this.interceptors = Collections.unmodifiableList(newInterceptors);
this.interceptorChain = current;
}

private List<ConfigSource> getSources(final List<ConfigSourceInterceptorWithPriority> interceptors) {
final List<ConfigSource> sources = interceptors.stream()
.map(ConfigSourceInterceptorWithPriority::getInterceptor)
.filter(SmallRyeConfigSourceInterceptor.class::isInstance)
.map(SmallRyeConfigSourceInterceptor.class::cast)
.map(SmallRyeConfigSourceInterceptor::getSource)
.collect(toList());
Collections.reverse(sources);
return sources;
}

List<ConfigSource> getSources() {
return sources;
}

List<ConfigSourceInterceptor> getInterceptors() {
List<ConfigSourceInterceptorWithPriority> getInterceptors() {
return interceptors;
}

ConfigSourceInterceptorContext getInterceptorChain() {
return interceptorChain;
}
}

static class ConfigSourceInterceptorWithPriority implements Comparable<ConfigSourceInterceptorWithPriority>, Serializable {
private static final long serialVersionUID = 1637460029437579033L;

final Function<ConfigSourceInterceptorContext, ConfigSourceInterceptor> interceptor;
final int priority;
final String name;

public ConfigSourceInterceptorWithPriority(final InterceptorWithPriority interceptor) {
this.interceptor = interceptor::getInterceptor;
this.priority = interceptor.getPriority();
this.name = "undefined";
}

public ConfigSourceInterceptorWithPriority(final ConfigSource configSource) {
this.interceptor = context -> configSourceInterceptor(configSource);
this.priority = configSource.getOrdinal();
this.name = configSource.getName();
}

public ConfigSourceInterceptorWithPriority(final ConfigSourceInterceptor interceptor, final int priority,
final String name) {
this.interceptor = (Serializable & Function) context -> interceptor;
this.priority = priority;
this.name = name;
}

public ConfigSourceInterceptor getInterceptor() {
return interceptor.apply(null);
}

public ConfigSourceInterceptorWithPriority getInterceptor(final ConfigSourceInterceptorContext context) {
return new ConfigSourceInterceptorWithPriority(interceptor.apply(context), priority, name);
}

public int getPriority() {
return priority;
}

@Override
public int compareTo(final ConfigSourceInterceptorWithPriority other) {
int res = Integer.compare(this.priority, other.priority);
return res != 0 ? res : this.name.compareTo(other.name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ List<ConfigSource> discoverSources() {
classLoader);
configSourceProviderLoader.forEach(configSourceProvider -> configSourceProvider.getConfigSources(classLoader)
.forEach(discoveredSources::add));

ServiceLoader<ConfigSourceFactory> configSourceFactoryLoader = ServiceLoader.load(ConfigSourceFactory.class,
classLoader);
configSourceFactoryLoader.forEach(this::withSources);

return discoveredSources;
}

Expand Down Expand Up @@ -186,6 +191,24 @@ public SmallRyeConfigBuilder withSources(Collection<ConfigSource> configSources)
return this;
}

public SmallRyeConfigBuilder withSources(ConfigSourceFactory... configSourceFactories) {
Stream.of(configSourceFactories).forEach(configSourceFactory -> {
interceptors.add(new InterceptorWithPriority(new ConfigSourceInterceptorFactory() {
@Override
public ConfigSourceInterceptor getInterceptor(final ConfigSourceInterceptorContext context) {
return SmallRyeConfigSourceInterceptor.configSourceInterceptor(
configSourceFactory.getSource(context::proceed));
}

@Override
public OptionalInt getPriority() {
return configSourceFactory.getPriority();
}
}));
});
return this;
}

public SmallRyeConfigBuilder withInterceptors(ConfigSourceInterceptor... interceptors) {
this.interceptors.addAll(Stream.of(interceptors)
.map(InterceptorWithPriority::new)
Expand Down Expand Up @@ -315,7 +338,8 @@ Converter<?> getConverter() {
}

static class InterceptorWithPriority {
static final OptionalInt OPTIONAL_DEFAULT_PRIORITY = OptionalInt.of(ConfigSourceInterceptorFactory.DEFAULT_PRIORITY);
private static final OptionalInt OPTIONAL_DEFAULT_PRIORITY = OptionalInt
.of(ConfigSourceInterceptorFactory.DEFAULT_PRIORITY);

private final ConfigSourceInterceptorFactory factory;
private final int priority;
Expand Down Expand Up @@ -346,11 +370,11 @@ private InterceptorWithPriority(ConfigSourceInterceptorFactory factory) {
this.priority = factory.getPriority().orElse(ConfigSourceInterceptorFactory.DEFAULT_PRIORITY);
}

public ConfigSourceInterceptor getInterceptor(ConfigSourceInterceptorContext context) {
ConfigSourceInterceptor getInterceptor(ConfigSourceInterceptorContext context) {
return factory.getInterceptor(context);
}

public int getPriority() {
int getPriority() {
return priority;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public Iterator<ConfigValue> iterateValues(final ConfigSourceInterceptorContext
return values.iterator();
}

public ConfigSource getSource() {
if (configSource instanceof ConfigValueConfigSourceWrapper) {
return ((ConfigValueConfigSourceWrapper) configSource).unwrap();
}

return configSource;
}

public static ConfigSourceInterceptor configSourceInterceptor(final ConfigSource configSource) {
return new SmallRyeConfigSourceInterceptor(configSource);
}
Expand Down
Loading