Skip to content

Commit

Permalink
Fix for #196. Support configurable Config Sources.
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Jun 11, 2020
1 parent 1c5dc19 commit 950a192
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.smallrye.config;

import io.smallrye.common.annotation.Experimental;

@Experimental("")
public interface ConfigSourceContext {
ConfigValue getValue(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.smallrye.config;

import java.util.OptionalInt;

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

import io.smallrye.common.annotation.Experimental;

@Experimental("")
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 @@ -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
Original file line number Diff line number Diff line change
@@ -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<String, String> getProperties() {
return null;
}

@Override
public String getValue(final String propertyName) {
return value;
}
};
}

@Override
public OptionalInt getPriority() {
return OptionalInt.of(1000);
}
})
.build();

final List<ConfigSource> 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<String, String> 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"));
}
}
12 changes: 5 additions & 7 deletions sources/zookeeper/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
<artifactId>smallrye-config-common</artifactId>
</dependency>

<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config</artifactId>
</dependency>

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
Expand Down Expand Up @@ -66,12 +71,6 @@
<artifactId>weld-junit5</artifactId>
</dependency>

<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
Expand All @@ -84,7 +83,6 @@
</exclusion>
</exclusions>
</dependency>

</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.smallrye.config.source.zookeeper;

public class ZooKeeperConfigException extends Exception {
public class ZooKeeperConfigException extends RuntimeException {

public ZooKeeperConfigException() {
super();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -23,26 +20,32 @@
public class ZooKeeperConfigSource extends AbstractConfigSource {
private static final long serialVersionUID = 3127679154588598693L;

//Apache Curator framework used to access Zookeeper
private AtomicReference<CuratorFramework> 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
Expand All @@ -51,7 +54,7 @@ public Set<String> getPropertyNames() {
final Set<String> propertyNames = new HashSet<>();

try {
final List<String> children = getCuratorClient().getChildren().forPath(applicationId);
final List<String> children = curator.getChildren().forPath(applicationId);
propertyNames.addAll(children);
} catch (Exception e) {
ZooKeepperLogging.log.failedToRetrievePropertyNames(e);
Expand All @@ -66,9 +69,9 @@ public Map<String, String> getProperties() {
final Map<String, String> props = new HashMap<>();

try {
final List<String> children = getCuratorClient().getChildren().forPath(applicationId);
final List<String> 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) {
Expand All @@ -80,20 +83,11 @@ public Map<String, String> 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;
}
Expand All @@ -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<String> zookeeperUrl = cfg.getOptionalValue(ZOOKEEPER_URL_KEY, String.class);
final Optional<String> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.smallrye.config.source.zookeeper.ZooKeeperConfigSourceFactory

This file was deleted.

0 comments on commit 950a192

Please sign in to comment.