diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc
index f9b223af0..d70219de0 100644
--- a/doc/modules/ROOT/nav.adoc
+++ b/doc/modules/ROOT/nav.adoc
@@ -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]
diff --git a/doc/modules/ROOT/pages/config-sources/config-sources.adoc b/doc/modules/ROOT/pages/config-sources/config-sources.adoc
index 39482df91..b3c5e606b 100644
--- a/doc/modules/ROOT/pages/config-sources/config-sources.adoc
+++ b/doc/modules/ROOT/pages/config-sources/config-sources.adoc
@@ -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:
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigSourceContext.java b/implementation/src/main/java/io/smallrye/config/ConfigSourceContext.java
new file mode 100644
index 000000000..1de649778
--- /dev/null
+++ b/implementation/src/main/java/io/smallrye/config/ConfigSourceContext.java
@@ -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);
+}
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigSourceFactory.java b/implementation/src/main/java/io/smallrye/config/ConfigSourceFactory.java
new file mode 100644
index 000000000..3179ad97a
--- /dev/null
+++ b/implementation/src/main/java/io/smallrye/config/ConfigSourceFactory.java
@@ -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}.
+ *
+ *
+ * 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}.
+ *
+ *
+ * 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();
+ }
+}
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptorFactory.java b/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptorFactory.java
index 061309766..db0ad34d3 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptorFactory.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigSourceInterceptorFactory.java
@@ -14,11 +14,10 @@
* initialized.
*
*
- * 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 {
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigValueConfigSourceWrapper.java b/implementation/src/main/java/io/smallrye/config/ConfigValueConfigSourceWrapper.java
index 34df75c17..5865f4fdc 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigValueConfigSourceWrapper.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigValueConfigSourceWrapper.java
@@ -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;
diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
index caae5b254..60676b042 100644
--- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
+++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java
@@ -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;
@@ -48,20 +49,8 @@
* @author Jeff Mesnil (c) 2017 Red Hat inc.
*/
public class SmallRyeConfig implements Config, Serializable {
-
private static final long serialVersionUID = 8138651532357898263L;
- static final Comparator CONFIG_SOURCE_COMPARATOR = new Comparator() {
- @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;
private final Map> converters;
private final Map>> optionalConverters = new ConcurrentHashMap<>();
@@ -229,14 +218,7 @@ public Iterable getConfigSources() {
*/
@Deprecated
public void addConfigSource(ConfigSource configSource) {
- configSources.updateAndGet(configSources -> {
- List currentSources = configSources.getSources();
-
- int oldSize = currentSources.size();
- List 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 convert(String value, Class asType) {
@@ -270,7 +252,7 @@ private static class ConfigSources implements Serializable {
private static final long serialVersionUID = 3483018375584151712L;
private final List sources;
- private final List interceptors;
+ private final List interceptors;
private final ConfigSourceInterceptorContext interceptorChain;
/**
@@ -285,23 +267,21 @@ private static class ConfigSources implements Serializable {
* @param interceptors the Interceptors to be part of Config.
*/
ConfigSources(final List sources, final List interceptors) {
- sources.sort(CONFIG_SOURCE_COMPARATOR);
- interceptors.sort(Comparator.comparingInt(InterceptorWithPriority::getPriority).reversed());
+ final List 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 initializedInterceptors = new ArrayList<>();
+ final List 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;
}
@@ -311,31 +291,42 @@ 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 sources, final ConfigSources configSources) {
- sources.sort(CONFIG_SOURCE_COMPARATOR);
+ ConfigSources(final ConfigSources sources, final ConfigSource configSource) {
+ final int oldSize = sources.getInterceptors().size();
+ final List 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 getSources(final List interceptors) {
+ final List 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 getSources() {
return sources;
}
- List getInterceptors() {
+ List getInterceptors() {
return interceptors;
}
@@ -343,4 +334,49 @@ ConfigSourceInterceptorContext getInterceptorChain() {
return interceptorChain;
}
}
+
+ static class ConfigSourceInterceptorWithPriority implements Comparable, Serializable {
+ private static final long serialVersionUID = 1637460029437579033L;
+
+ final Function 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);
+ }
+ }
}
diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java
index e57f2b321..feb8020d9 100644
--- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java
+++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigBuilder.java
@@ -92,6 +92,11 @@ List discoverSources() {
classLoader);
configSourceProviderLoader.forEach(configSourceProvider -> configSourceProvider.getConfigSources(classLoader)
.forEach(discoveredSources::add));
+
+ ServiceLoader configSourceFactoryLoader = ServiceLoader.load(ConfigSourceFactory.class,
+ classLoader);
+ configSourceFactoryLoader.forEach(this::withSources);
+
return discoveredSources;
}
@@ -186,6 +191,24 @@ public SmallRyeConfigBuilder withSources(Collection 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)
@@ -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;
@@ -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;
}
}
diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigSourceInterceptor.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigSourceInterceptor.java
index 838fffbb0..fd52bc665 100644
--- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfigSourceInterceptor.java
+++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfigSourceInterceptor.java
@@ -43,6 +43,14 @@ public Iterator 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);
}
diff --git a/implementation/src/test/java/io/smallrye/config/ConfigConfigSourceTest.java b/implementation/src/test/java/io/smallrye/config/ConfigConfigSourceTest.java
new file mode 100644
index 000000000..cfbf39580
--- /dev/null
+++ b/implementation/src/test/java/io/smallrye/config/ConfigConfigSourceTest.java
@@ -0,0 +1,94 @@
+package io.smallrye.config;
+
+import static java.util.stream.Collectors.toList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.microprofile.config.spi.ConfigSource;
+import org.junit.jupiter.api.Test;
+
+import io.smallrye.config.common.AbstractConfigSource;
+
+public class ConfigConfigSourceTest {
+ @Test
+ public void configure() {
+ final SmallRyeConfig config = new SmallRyeConfigBuilder()
+ .addDefaultSources()
+ .addDefaultInterceptors()
+ .withSources(KeyValuesConfigSource.config("my.prop", "1234"))
+ .withSources(new ConfigSourceFactory() {
+ @Override
+ public ConfigSource getSource(final ConfigSourceContext context) {
+ return new AbstractConfigSource("test", 1000) {
+ final String value = context.getValue("my.prop").getValue();
+
+ @Override
+ public Map getProperties() {
+ return null;
+ }
+
+ @Override
+ public String getValue(final String propertyName) {
+ return value;
+ }
+ };
+ }
+
+ @Override
+ public OptionalInt getPriority() {
+ return OptionalInt.of(1000);
+ }
+ })
+ .build();
+
+ final List configSources = StreamSupport.stream(config.getConfigSources().spliterator(), false)
+ .collect(toList());
+ assertEquals(1, configSources.stream().filter(source -> source.getName().equals("test")).count());
+
+ assertEquals("1234", config.getRawValue("my.prop"));
+ assertEquals("test", config.getConfigValue("my.prop").getConfigSourceName());
+ assertEquals("1234", config.getRawValue("any"));
+ assertEquals("test", config.getConfigValue("any").getConfigSourceName());
+ }
+
+ @Test
+ public void lowerPriority() {
+ final SmallRyeConfig config = new SmallRyeConfigBuilder()
+ .addDefaultSources()
+ .addDefaultInterceptors()
+ .withSources(KeyValuesConfigSource.config("my.prop", "1234"))
+ .withSources(new ConfigSourceFactory() {
+ @Override
+ public ConfigSource getSource(final ConfigSourceContext context) {
+ return new AbstractConfigSource("test", 0) {
+ final ConfigValue value = context.getValue("my.prop");
+
+ @Override
+ public Map getProperties() {
+ return null;
+ }
+
+ @Override
+ public String getValue(final String propertyName) {
+ return value != null ? value.getValue() : null;
+ }
+ };
+ }
+
+ @Override
+ public OptionalInt getPriority() {
+ return OptionalInt.of(0);
+ }
+ })
+ .build();
+
+ assertEquals("1234", config.getRawValue("my.prop"));
+ assertEquals("KeyValuesConfigSource", config.getConfigValue("my.prop").getConfigSourceName());
+ assertNull(config.getRawValue("any"));
+ }
+}
diff --git a/implementation/src/test/java/io/smallrye/config/ConfigSourceSortTestCase.java b/implementation/src/test/java/io/smallrye/config/ConfigSourceSortTestCase.java
deleted file mode 100644
index 41d910ea6..000000000
--- a/implementation/src/test/java/io/smallrye/config/ConfigSourceSortTestCase.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2019 Red Hat, Inc., and individual contributors
- * as indicated by the @author tags.
- *
- * 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.smallrye.config;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.Comparator;
-import java.util.Map;
-
-import org.eclipse.microprofile.config.spi.ConfigSource;
-import org.junit.jupiter.api.Test;
-
-/**
- *
- */
-public class ConfigSourceSortTestCase {
-
- static final Comparator CMP = SmallRyeConfig.CONFIG_SOURCE_COMPARATOR;
-
- static final class DummyConfigSource implements ConfigSource {
- private final int ordinal;
-
- DummyConfigSource(final int ordinal) {
- this.ordinal = ordinal;
- }
-
- public int getOrdinal() {
- return ordinal;
- }
-
- public Map getProperties() {
- return null;
- }
-
- public String getValue(final String propertyName) {
- return null;
- }
-
- public String getName() {
- return "placeholder";
- }
- }
-
- static ConfigSource src(int ordinal) {
- return new DummyConfigSource(ordinal);
- }
-
- @Test
- public void ensureCorrectOrdinalSort() {
- assertEquals(1, CMP.compare(src(100), src(200)));
- assertEquals(1, CMP.compare(src(-100), src(200)));
- assertEquals(0, CMP.compare(src(0), src(0)));
- assertEquals(0, CMP.compare(src(-100), src(-100)));
- assertEquals(1, CMP.compare(src(0), src(Integer.MAX_VALUE)));
- assertEquals(1, CMP.compare(src(Integer.MIN_VALUE), src(0)));
- assertEquals(-1, CMP.compare(src(0), src(Integer.MIN_VALUE)));
- assertEquals(-1, CMP.compare(src(Integer.MAX_VALUE), src(0)));
- }
-}
diff --git a/implementation/src/test/java/io/smallrye/config/ConfigSourceWrapperTestCase.java b/implementation/src/test/java/io/smallrye/config/ConfigSourceWrapperTestCase.java
index f66591562..b0835c669 100644
--- a/implementation/src/test/java/io/smallrye/config/ConfigSourceWrapperTestCase.java
+++ b/implementation/src/test/java/io/smallrye/config/ConfigSourceWrapperTestCase.java
@@ -105,6 +105,11 @@ public String getValue(final String propertyName) {
public String getName() {
return delegate.getName();
}
+
+ @Override
+ public int getOrdinal() {
+ return delegate.getOrdinal();
+ }
}
static T assertIsInstance(Class expected, Object thing) {
diff --git a/sources/zookeeper/pom.xml b/sources/zookeeper/pom.xml
index a3d2a7638..a70acbc39 100644
--- a/sources/zookeeper/pom.xml
+++ b/sources/zookeeper/pom.xml
@@ -39,6 +39,11 @@
smallrye-config-common
+
+ io.smallrye.config
+ smallrye-config
+
+
org.apache.curator
curator-recipes
@@ -66,12 +71,6 @@
weld-junit5
-
- io.smallrye.config
- smallrye-config
- test
-
-
org.apache.curator
curator-test
@@ -84,7 +83,6 @@
-
diff --git a/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigException.java b/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigException.java
index d8ee2ec6a..c756c58e2 100644
--- a/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigException.java
+++ b/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigException.java
@@ -1,6 +1,6 @@
package io.smallrye.config.source.zookeeper;
-public class ZooKeeperConfigException extends Exception {
+public class ZooKeeperConfigException extends RuntimeException {
public ZooKeeperConfigException() {
super();
diff --git a/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSource.java b/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSource.java
index 5727e59ce..70668a3ad 100644
--- a/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSource.java
+++ b/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSource.java
@@ -1,14 +1,11 @@
package io.smallrye.config.source.zookeeper;
import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
-import org.eclipse.microprofile.config.Config;
-import org.eclipse.microprofile.config.ConfigProvider;
import io.smallrye.config.common.AbstractConfigSource;
@@ -23,26 +20,32 @@
public class ZooKeeperConfigSource extends AbstractConfigSource {
private static final long serialVersionUID = 3127679154588598693L;
- //Apache Curator framework used to access Zookeeper
- private AtomicReference curatorReference = new AtomicReference<>();
-
- //Root node of an application's configuration
- private String applicationId;
-
- //Prefix of ignored properties
- private static final String IGNORED_PREFIX = "io.smallrye.configsource.zookeeper";
-
//Property the URL of the Zookeeper instance will be read from
static final String ZOOKEEPER_URL_KEY = "io.smallrye.configsource.zookeeper.url";
-
//Property of the Application Id. This will be the root znode for an application's properties
static final String APPLICATION_ID_KEY = "io.smallrye.configsource.zookeeper.applicationId";
//Name of this ConfigSource
private static final String ZOOKEEPER_CONFIG_SOURCE_NAME = "io.smallrye.configsource.zookeeper";
- public ZooKeeperConfigSource() {
+ //Apache Curator framework used to access Zookeeper
+ private final CuratorFramework curator;
+ //Root node of an application's configuration
+ private final String applicationId;
+
+ public ZooKeeperConfigSource(final String zookeeperUrl, final String applicationId) {
super(ZOOKEEPER_CONFIG_SOURCE_NAME, 150);
+
+ //Only create the ZK Client if the properties exist.
+ if (zookeeperUrl != null && applicationId != null) {
+ ZooKeepperLogging.log.configuringZookeeper(zookeeperUrl, applicationId);
+
+ this.applicationId = applicationId.startsWith("/") ? applicationId : "/" + applicationId;
+ this.curator = CuratorFrameworkFactory.newClient(zookeeperUrl, new ExponentialBackoffRetry(1000, 3));
+ this.curator.start();
+ } else {
+ throw ZooKeeperMessages.msg.propertiesNotSet();
+ }
}
@Override
@@ -51,7 +54,7 @@ public Set getPropertyNames() {
final Set propertyNames = new HashSet<>();
try {
- final List children = getCuratorClient().getChildren().forPath(applicationId);
+ final List children = curator.getChildren().forPath(applicationId);
propertyNames.addAll(children);
} catch (Exception e) {
ZooKeepperLogging.log.failedToRetrievePropertyNames(e);
@@ -66,9 +69,9 @@ public Map getProperties() {
final Map props = new HashMap<>();
try {
- final List children = getCuratorClient().getChildren().forPath(applicationId);
+ final List children = curator.getChildren().forPath(applicationId);
for (final String key : children) {
- final String value = new String(getCuratorClient().getData().forPath(applicationId + "/" + key));
+ final String value = new String(curator.getData().forPath(applicationId + "/" + key));
props.put(key, value);
}
} catch (Exception e) {
@@ -80,20 +83,11 @@ public Map getProperties() {
@Override
public String getValue(final String key) {
-
- /*
- * Explicitly ignore all keys that are prefixed with the prefix used to configure the Zookeeper connection.
- * Other wise a stack overflow obviously happens.
- */
- // TODO - radcortez - We need to add a feature that allows ConfigSource to config itself with other ConfigSource
- if (key.startsWith(IGNORED_PREFIX) || key.startsWith("smallrye.config")) {
- return null;
- }
try {
- final Stat stat = getCuratorClient().checkExists().forPath(applicationId + "/" + key);
+ final Stat stat = curator.checkExists().forPath(applicationId + "/" + key);
if (stat != null) {
- return new String(getCuratorClient().getData().forPath(applicationId + "/" + key));
+ return new String(curator.getData().forPath(applicationId + "/" + key));
} else {
return null;
}
@@ -102,35 +96,4 @@ public String getValue(final String key) {
}
return null;
}
-
- private CuratorFramework getCuratorClient() throws ZooKeeperConfigException {
-
- CuratorFramework cachedClient = curatorReference.get();
- if (cachedClient == null) {
-
- final Config cfg = ConfigProvider.getConfig();
-
- final Optional zookeeperUrl = cfg.getOptionalValue(ZOOKEEPER_URL_KEY, String.class);
- final Optional optApplicationId = cfg.getOptionalValue(APPLICATION_ID_KEY, String.class);
-
- //Only create the ZK Client if the properties exist.
- if (zookeeperUrl.isPresent() && optApplicationId.isPresent()) {
-
- ZooKeepperLogging.log.configuringZookeeper(zookeeperUrl.get(), optApplicationId.get());
-
- applicationId = optApplicationId.get();
-
- if (!applicationId.startsWith("/")) {
- applicationId = "/" + applicationId;
- }
- cachedClient = CuratorFrameworkFactory.newClient(zookeeperUrl.get(), new ExponentialBackoffRetry(1000, 3));
- curatorReference.compareAndSet(null, cachedClient);
- cachedClient.start();
-
- } else {
- throw ZooKeeperMessages.msg.propertiesNotSet();
- }
- }
- return cachedClient;
- }
}
diff --git a/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSourceFactory.java b/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSourceFactory.java
new file mode 100644
index 000000000..1d23a9368
--- /dev/null
+++ b/sources/zookeeper/src/main/java/io/smallrye/config/source/zookeeper/ZooKeeperConfigSourceFactory.java
@@ -0,0 +1,21 @@
+package io.smallrye.config.source.zookeeper;
+
+import java.util.OptionalInt;
+
+import org.eclipse.microprofile.config.spi.ConfigSource;
+
+import io.smallrye.config.ConfigSourceContext;
+import io.smallrye.config.ConfigSourceFactory;
+
+public class ZooKeeperConfigSourceFactory implements ConfigSourceFactory {
+ @Override
+ public ConfigSource getSource(final ConfigSourceContext context) {
+ return new ZooKeeperConfigSource(context.getValue(ZooKeeperConfigSource.ZOOKEEPER_URL_KEY).getValue(),
+ context.getValue(ZooKeeperConfigSource.APPLICATION_ID_KEY).getValue());
+ }
+
+ @Override
+ public OptionalInt getPriority() {
+ return OptionalInt.of(150);
+ }
+}
diff --git a/sources/zookeeper/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory b/sources/zookeeper/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory
new file mode 100644
index 000000000..ebc5feedb
--- /dev/null
+++ b/sources/zookeeper/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory
@@ -0,0 +1 @@
+io.smallrye.config.source.zookeeper.ZooKeeperConfigSourceFactory
diff --git a/sources/zookeeper/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/sources/zookeeper/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
deleted file mode 100644
index 419007878..000000000
--- a/sources/zookeeper/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
+++ /dev/null
@@ -1 +0,0 @@
-io.smallrye.config.source.zookeeper.ZooKeeperConfigSource
\ No newline at end of file