diff --git a/consul/deployment/src/main/java/io/quarkus/consul/config/ConsulConfigProcessor.java b/consul/deployment/src/main/java/io/quarkus/consul/config/ConsulConfigProcessor.java index 55642c0..7c0e3a5 100644 --- a/consul/deployment/src/main/java/io/quarkus/consul/config/ConsulConfigProcessor.java +++ b/consul/deployment/src/main/java/io/quarkus/consul/config/ConsulConfigProcessor.java @@ -2,18 +2,15 @@ import org.jsoup.Connection.Response; -import io.quarkus.consul.config.runtime.ConsulConfigRecorder; +import io.quarkus.consul.config.runtime.ConsulConfigSourceFactoryBuilder; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.ExecutionTime; -import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.builditem.RunTimeConfigurationSourceValueBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; public class ConsulConfigProcessor { - private static final String FEATURE = "consul-config"; @BuildStep @@ -32,10 +29,7 @@ public void registerForReflection(BuildProducer reflec } @BuildStep - @Record(ExecutionTime.RUNTIME_INIT) - public RunTimeConfigurationSourceValueBuildItem configure(ConsulConfigRecorder recorder) { - return new RunTimeConfigurationSourceValueBuildItem( - recorder.configSources()); + void consulConfigFactory(BuildProducer runTimeConfigBuilder) { + runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(ConsulConfigSourceFactoryBuilder.class.getName())); } - } diff --git a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfig.java b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfig.java index 9803765..ab857f2 100644 --- a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfig.java +++ b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfig.java @@ -3,149 +3,132 @@ import java.net.InetSocketAddress; import java.nio.file.Path; import java.time.Duration; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.runtime.configuration.DurationConverter; +import io.quarkus.runtime.configuration.InetSocketAddressConverter; +import io.quarkus.runtime.configuration.PathConverter; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithConverter; +import io.smallrye.config.WithDefault; -@ConfigRoot(name = "consul-config", phase = ConfigPhase.BOOTSTRAP) -public class ConsulConfig { +@ConfigMapping(prefix = "quarkus.consul-config") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface ConsulConfig { /** * If set to true, the application will attempt to look up the configuration from Consul */ - @ConfigItem(defaultValue = "false") - boolean enabled; + @WithDefault("false") + boolean enabled(); /** * Consul agent related configuration */ - @ConfigItem - AgentConfig agent; + AgentConfig agent(); /** * Common prefix that all keys share when looking up the keys from Consul. * The prefix is not included in the keys of the user configuration */ - @ConfigItem - Optional prefix; + Optional prefix(); /** * Keys whose value is a raw string. * When this is used, the keys that end up in the user configuration are the keys specified her with '/' replaced by '.' */ - @ConfigItem - Optional> rawValueKeys; + Optional> rawValueKeys(); /** * Keys whose value represents a properties file. * When this is used, the keys that end up in the user configuration are the keys of the properties file, * not these keys */ - @ConfigItem - Optional> propertiesValueKeys; + Optional> propertiesValueKeys(); /** * If set to true, the application will not start if any of the configured config sources cannot be located */ - @ConfigItem(defaultValue = "true") - boolean failOnMissingKey; - - Map keysAsMap() { - Map result = new LinkedHashMap<>(); - if (rawValueKeys.isPresent()) { - for (String key : rawValueKeys.get()) { - result.put(key, ValueType.RAW); - } - } - if (propertiesValueKeys.isPresent()) { - for (String key : propertiesValueKeys.get()) { - result.put(key, ValueType.PROPERTIES); - } - } - return result; - } + @WithDefault("true") + boolean failOnMissingKey(); @ConfigGroup - public static class AgentConfig { + interface AgentConfig { /** * Consul agent host */ - @ConfigItem(defaultValue = "localhost:8500") - InetSocketAddress hostPort; + @WithDefault("localhost:8500") + @WithConverter(InetSocketAddressConverter.class) + InetSocketAddress hostPort(); /** * Whether or not to use HTTPS when communicating with the agent */ - @ConfigItem(defaultValue = "false") - boolean useHttps; + @WithDefault("false") + boolean useHttps(); /** * Consul token to be provided when authentication is enabled */ - @ConfigItem - Optional token; + Optional token(); /** * TrustStore to be used containing the SSL certificate used by Consul agent * Can be either a classpath resource or a file system path */ - @ConfigItem - public Optional trustStore; + @WithConverter(PathConverter.class) + Optional trustStore(); /** * Password of TrustStore to be used containing the SSL certificate used by Consul agent */ - @ConfigItem - public Optional trustStorePassword; + Optional trustStorePassword(); /** * KeyStore to be used containing the SSL certificate for authentication with Consul agent * Can be either a classpath resource or a file system path */ - @ConfigItem - public Optional keyStore; + @WithConverter(PathConverter.class) + Optional keyStore(); /** * Password of KeyStore to be used containing the SSL certificate for authentication with Consul agent */ - @ConfigItem - public Optional keyStorePassword; + Optional keyStorePassword(); /** * Password to recover key from KeyStore for SSL client authentication with Consul agent * If no value is provided, the key-store-password will be used */ - @ConfigItem - public Optional keyPassword; + Optional keyPassword(); /** * When using HTTPS and no keyStore has been specified, whether or not to trust all certificates */ - @ConfigItem(defaultValue = "false") - boolean trustCerts; + @WithDefault("false") + boolean trustCerts(); /** * The amount of time to wait when initially establishing a connection before giving up and timing out. *

* Specify `0` to wait indefinitely. */ - @ConfigItem(defaultValue = "10S") - public Duration connectionTimeout; + @WithDefault("10s") + @WithConverter(DurationConverter.class) + Duration connectionTimeout(); /** * The amount of time to wait for a read on a socket before an exception is thrown. *

* Specify `0` to wait indefinitely. */ - @ConfigItem(defaultValue = "60S") - public Duration readTimeout; + @WithDefault("60s") + @WithConverter(DurationConverter.class) + Duration readTimeout(); } - } diff --git a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigRecorder.java b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigRecorder.java deleted file mode 100644 index e26e975..0000000 --- a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigRecorder.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.quarkus.consul.config.runtime; - -import java.util.Collections; - -import org.eclipse.microprofile.config.spi.ConfigSource; -import org.eclipse.microprofile.config.spi.ConfigSourceProvider; -import org.jboss.logging.Logger; - -import io.quarkus.runtime.RuntimeValue; -import io.quarkus.runtime.annotations.Recorder; - -@Recorder -public class ConsulConfigRecorder { - - private static final Logger log = Logger.getLogger(ConsulConfigRecorder.class); - - final ConsulConfig consulConfig; - - public ConsulConfigRecorder(ConsulConfig consulConfig) { - this.consulConfig = consulConfig; - } - - public RuntimeValue configSources() { - if (!consulConfig.enabled) { - log.debug( - "No attempt will be made to obtain configuration from Consul because the functionality has been disabled via configuration"); - return emptyRuntimeValue(); - } - - return new RuntimeValue<>( - new ConsulConfigSourceProvider(consulConfig)); - } - - private RuntimeValue emptyRuntimeValue() { - return new RuntimeValue<>(new EmptyConfigSourceProvider()); - } - - private static class EmptyConfigSourceProvider implements ConfigSourceProvider { - - @Override - public Iterable getConfigSources(ClassLoader forClassLoader) { - return Collections.emptyList(); - } - } -} diff --git a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceProvider.java b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactory.java similarity index 52% rename from consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceProvider.java rename to consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactory.java index 8bd60a0..0d9d3d2 100644 --- a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceProvider.java +++ b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactory.java @@ -2,46 +2,44 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletionException; import java.util.function.Consumer; import org.eclipse.microprofile.config.spi.ConfigSource; -import org.eclipse.microprofile.config.spi.ConfigSourceProvider; import org.jboss.logging.Logger; +import io.smallrye.config.ConfigSourceContext; +import io.smallrye.config.ConfigSourceFactory; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.groups.UniAwait; -class ConsulConfigSourceProvider implements ConfigSourceProvider { +class ConsulConfigSourceFactory implements ConfigSourceFactory.ConfigurableConfigSourceFactory { + private static final Logger log = Logger.getLogger(ConsulConfigSourceFactory.class); - private static final Logger log = Logger.getLogger(ConsulConfigSourceProvider.class); - - private final ConsulConfig config; - - private final ConsulConfigGateway consulConfigGateway; - private final ResponseConfigSourceUtil responseConfigSourceUtil; - - public ConsulConfigSourceProvider(ConsulConfig config) { - this(config, new VertxConsulConfigGateway(config), new ResponseConfigSourceUtil()); - } - - // visible for testing - ConsulConfigSourceProvider(ConsulConfig config, ConsulConfigGateway consulConfigGateway) { - this(config, consulConfigGateway, new ResponseConfigSourceUtil()); - } + @Override + public Iterable getConfigSources(final ConfigSourceContext context, final ConsulConfig config) { + if (!config.enabled()) { + return Collections.emptyList(); + } - private ConsulConfigSourceProvider(ConsulConfig config, ConsulConfigGateway consulConfigGateway, - ResponseConfigSourceUtil responseConfigSourceUtil) { - this.config = config; - this.consulConfigGateway = consulConfigGateway; - this.responseConfigSourceUtil = responseConfigSourceUtil; + return getConfigSources(config, new VertxConsulConfigGateway(config)); } - @Override - public Iterable getConfigSources(ClassLoader cl) { - Map keys = config.keysAsMap(); + Iterable getConfigSources(ConsulConfig config, ConsulConfigGateway consulConfigGateway) { + Map keys = new LinkedHashMap<>(); + if (config.rawValueKeys().isPresent()) { + for (String key : config.rawValueKeys().get()) { + keys.put(key, ValueType.RAW); + } + } + if (config.propertiesValueKeys().isPresent()) { + for (String key : config.propertiesValueKeys().get()) { + keys.put(key, ValueType.PROPERTIES); + } + } if (keys.isEmpty()) { log.debug("No keys were configured for config source lookup"); return Collections.emptyList(); @@ -52,17 +50,15 @@ public Iterable getConfigSources(ClassLoader cl) { List> allUnis = new ArrayList<>(); for (Map.Entry entry : keys.entrySet()) { - String fullKey = config.prefix.isPresent() ? config.prefix.get() + "/" + entry.getKey() : entry.getKey(); + String fullKey = config.prefix().isPresent() ? config.prefix().get() + "/" + entry.getKey() : entry.getKey(); allUnis.add(consulConfigGateway.getValue(fullKey).invoke(new Consumer() { @Override public void accept(Response response) { if (response != null) { - result.add( - responseConfigSourceUtil.toConfigSource(response, entry.getValue(), - config.prefix)); + result.add(ResponseConfigSourceUtil.toConfigSource(response, entry.getValue(), config.prefix())); } else { String message = "Key '" + fullKey + "' not found in Consul."; - if (config.failOnMissingKey) { + if (config.failOnMissingKey()) { throw new RuntimeException(message); } else { log.info(message); @@ -74,10 +70,10 @@ public void accept(Response response) { try { UniAwait await = Uni.combine().all().unis(allUnis).discardItems().await(); - if (config.agent.connectionTimeout.isZero() && config.agent.readTimeout.isZero()) { + if (config.agent().connectionTimeout().isZero() && config.agent().readTimeout().isZero()) { await.indefinitely(); } else { - await.atMost(config.agent.connectionTimeout.plus(config.agent.readTimeout.multipliedBy(2))); + await.atMost(config.agent().connectionTimeout().plus(config.agent().readTimeout().multipliedBy(2))); } } catch (CompletionException e) { throw new RuntimeException("An error occurred while attempting to fetch configuration from Consul.", e); diff --git a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactoryBuilder.java b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactoryBuilder.java new file mode 100644 index 0000000..478d963 --- /dev/null +++ b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactoryBuilder.java @@ -0,0 +1,11 @@ +package io.quarkus.consul.config.runtime; + +import io.quarkus.runtime.configuration.ConfigBuilder; +import io.smallrye.config.SmallRyeConfigBuilder; + +public class ConsulConfigSourceFactoryBuilder implements ConfigBuilder { + @Override + public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { + return builder.withSources(new ConsulConfigSourceFactory()); + } +} diff --git a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ResponseConfigSourceUtil.java b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ResponseConfigSourceUtil.java index 68f92e5..1ee478b 100644 --- a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ResponseConfigSourceUtil.java +++ b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/ResponseConfigSourceUtil.java @@ -19,7 +19,7 @@ class ResponseConfigSourceUtil { private static final int ORDINAL = 270; // this is higher than the file system or jar ordinals, but lower than env vars - public ConfigSource toConfigSource(Response response, ValueType valueType, Optional prefix) { + public static ConfigSource toConfigSource(Response response, ValueType valueType, Optional prefix) { if (log.isDebugEnabled()) { log.debug("Attempting to convert data of key " + " '" + response.getKey() + "' to a list of ConfigSource objects"); @@ -40,7 +40,7 @@ public ConfigSource toConfigSource(Response response, ValueType valueType, Optio return result; } - private String keyWithoutPrefix(Response response, Optional prefix) { + private static String keyWithoutPrefix(Response response, Optional prefix) { return prefix.isPresent() ? response.getKey().replace(prefix.get() + "/", "") : response.getKey(); } diff --git a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/VertxConsulConfigGateway.java b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/VertxConsulConfigGateway.java index fc58433..23dcff8 100644 --- a/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/VertxConsulConfigGateway.java +++ b/consul/runtime/src/main/java/io/quarkus/consul/config/runtime/VertxConsulConfigGateway.java @@ -37,7 +37,7 @@ public class VertxConsulConfigGateway implements ConsulConfigGateway { public VertxConsulConfigGateway(ConsulConfig consulConfig) { this.consulConfig = consulConfig; this.vertx = createVertxInstance(); - this.webClient = createHttpClient(vertx, consulConfig.agent); + this.webClient = createHttpClient(vertx, consulConfig.agent()); } private Vertx createVertxInstance() { @@ -64,15 +64,15 @@ private Vertx createVertxInstance() { public static WebClient createHttpClient(Vertx vertx, ConsulConfig.AgentConfig agentConfig) { WebClientOptions webClientOptions = new WebClientOptions() - .setConnectTimeout((int) agentConfig.connectionTimeout.toMillis()) - .setIdleTimeout((int) agentConfig.readTimeout.getSeconds()); + .setConnectTimeout((int) agentConfig.connectionTimeout().toMillis()) + .setIdleTimeout((int) agentConfig.readTimeout().getSeconds()); - boolean trustAll = agentConfig.trustCerts; + boolean trustAll = agentConfig.trustCerts(); try { - if (agentConfig.trustStore.isPresent()) { - Path trustStorePath = agentConfig.trustStore.get(); + if (agentConfig.trustStore().isPresent()) { + Path trustStorePath = agentConfig.trustStore().get(); String type = determineStoreType(trustStorePath); - KeyStoreOptionsBase storeOptions = storeOptions(trustStorePath, agentConfig.trustStorePassword, + KeyStoreOptionsBase storeOptions = storeOptions(trustStorePath, agentConfig.trustStorePassword(), createStoreOptions(type)); if (isPfx(type)) { webClientOptions.setPfxTrustOptions((PfxOptions) storeOptions); @@ -81,10 +81,10 @@ public static WebClient createHttpClient(Vertx vertx, ConsulConfig.AgentConfig a } } else if (trustAll) { skipVerify(webClientOptions); - } else if (agentConfig.keyStore.isPresent()) { - Path trustStorePath = agentConfig.keyStore.get(); + } else if (agentConfig.keyStore().isPresent()) { + Path trustStorePath = agentConfig.keyStore().get(); String type = determineStoreType(trustStorePath); - KeyStoreOptionsBase storeOptions = storeOptions(trustStorePath, agentConfig.keyStorePassword, + KeyStoreOptionsBase storeOptions = storeOptions(trustStorePath, agentConfig.keyStorePassword(), createStoreOptions(type)); if (isPfx(type)) { webClientOptions.setPfxTrustOptions((PfxOptions) storeOptions); @@ -159,11 +159,12 @@ private static byte[] allBytes(InputStream inputStream) throws Exception { @Override public Uni getValue(String key) { HttpRequest request = webClient - .get(consulConfig.agent.hostPort.getPort(), consulConfig.agent.hostPort.getHostString(), "/v1/kv/" + key) - .ssl(consulConfig.agent.useHttps) + .get(consulConfig.agent().hostPort().getPort(), consulConfig.agent().hostPort().getHostString(), + "/v1/kv/" + key) + .ssl(consulConfig.agent().useHttps()) .putHeader("Accept", "application/json;charset=UTF-8"); - if (consulConfig.agent.token.isPresent()) { - request.putHeader("Authorization", "Bearer " + consulConfig.agent.token.get()); + if (consulConfig.agent().token().isPresent()) { + request.putHeader("Authorization", "Bearer " + consulConfig.agent().token().get()); } log.debug("Attempting to look up value of key '" + key + "' from Consul."); diff --git a/consul/runtime/src/test/java/io/quarkus/consul/config/runtime/ConsulConfigSourceProviderTest.java b/consul/runtime/src/test/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactoryTest.java similarity index 70% rename from consul/runtime/src/test/java/io/quarkus/consul/config/runtime/ConsulConfigSourceProviderTest.java rename to consul/runtime/src/test/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactoryTest.java index fff9a6a..0bb050d 100644 --- a/consul/runtime/src/test/java/io/quarkus/consul/config/runtime/ConsulConfigSourceProviderTest.java +++ b/consul/runtime/src/test/java/io/quarkus/consul/config/runtime/ConsulConfigSourceFactoryTest.java @@ -12,8 +12,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.IOException; -import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -21,18 +19,17 @@ import org.eclipse.microprofile.config.spi.ConfigSource; import org.junit.jupiter.api.Test; -class ConsulConfigSourceProviderTest { +import io.quarkus.consul.config.runtime.ConsulConfig.AgentConfig; +class ConsulConfigSourceFactoryTest { private static final int EXPECTED_ORDINAL = 270; @Test - void testEmptyKeys() throws IOException { - ConsulConfig config = defaultConfig(); - + void testEmptyKeys() { + ConsulConfig config = mock(ConsulConfig.class); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - Iterable configSources = sut.getConfigSources(null); + Iterable configSources = new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); assertThat(configSources).isEmpty(); // no interactions with Consul should have taken place @@ -40,21 +37,21 @@ void testEmptyKeys() throws IOException { } @Test - void testWithMissingKeysAndFailureConfigured() throws IOException { - ConsulConfig config = defaultConfig(); - config.rawValueKeys = keyValues("some/first", "some/second"); - config.failOnMissingKey = true; + void testWithMissingKeysAndFailureConfigured() { + ConsulConfig config = mock(ConsulConfig.class); + when(config.rawValueKeys()).thenReturn(keyValues("some/first", "some/second")); + when(config.failOnMissingKey()).thenReturn(true); + AgentConfig agentConfig = mock(AgentConfig.class); + when(config.agent()).thenReturn(agentConfig); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); - // make sure the first is is properly resolved + // make sure the first is properly resolved when(mockGateway.getValue("some/first")).thenReturn(validResponse("some/first", "whatever")); // make sure the second is not resolved when(mockGateway.getValue("some/second")).thenReturn(emptyResponse()); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - assertThatThrownBy(() -> { - sut.getConfigSources(null); + new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); }).isInstanceOf(RuntimeException.class).hasMessageContaining("some/second"); //both of the keys should have been resolved because we resolve keys in the order they were given by the user @@ -63,22 +60,21 @@ void testWithMissingKeysAndFailureConfigured() throws IOException { } @Test - void testWithMissingKeysAndIgnoreFailureConfigured() throws IOException { - ConsulConfig config = defaultConfig(); - config.rawValueKeys = keyValues("some/first", "some/second", "some/third"); - config.failOnMissingKey = false; + void testWithMissingKeysAndIgnoreFailureConfigured() { + ConsulConfig config = mock(ConsulConfig.class); + when(config.rawValueKeys()).thenReturn(keyValues("some/first", "some/second", "some/third")); + AgentConfig agentConfig = mock(AgentConfig.class); + when(config.agent()).thenReturn(agentConfig); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); - // make sure the first is is properly resolved + // make sure the first is properly resolved when(mockGateway.getValue("some/first")).thenReturn(validResponse("some/first", "whatever")); // make sure the second is not resolved when(mockGateway.getValue("some/second")).thenReturn(emptyResponse()); - // make sure the third is is properly resolved + // make sure the third is properly resolved when(mockGateway.getValue("some/third")).thenReturn(validResponse("some/third", "other")); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - - Iterable configSources = sut.getConfigSources(null); + Iterable configSources = new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); assertThat(configSources).hasSize(2); assertThat(configSources).filteredOn(c -> c.getName().contains("first")).singleElement().satisfies(c -> { assertThat(c.getOrdinal()).isEqualTo(EXPECTED_ORDINAL); @@ -96,9 +92,11 @@ void testWithMissingKeysAndIgnoreFailureConfigured() throws IOException { } @Test - void testRawKeysWithoutPrefix() throws IOException { - ConsulConfig config = defaultConfig(); - config.rawValueKeys = keyValues("greeting/message", "greeting/name"); + void testRawKeysWithoutPrefix() { + ConsulConfig config = mock(ConsulConfig.class); + when(config.rawValueKeys()).thenReturn(keyValues("greeting/message", "greeting/name")); + AgentConfig agentConfig = mock(AgentConfig.class); + when(config.agent()).thenReturn(agentConfig); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); when(mockGateway.getValue("greeting/message")) @@ -106,9 +104,7 @@ void testRawKeysWithoutPrefix() throws IOException { when(mockGateway.getValue("greeting/name")) .thenReturn(validResponse("greeting/name", "quarkus")); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - - Iterable configSources = sut.getConfigSources(null); + Iterable configSources = new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); assertThat(configSources).hasSize(2); assertThat(configSources).filteredOn(c -> c.getName().contains("message")).singleElement().satisfies(c -> { assertThat(c.getOrdinal()).isEqualTo(EXPECTED_ORDINAL); @@ -124,10 +120,12 @@ void testRawKeysWithoutPrefix() throws IOException { } @Test - void testRawKeysWithPrefix() throws IOException { - ConsulConfig config = defaultConfig(); - config.prefix = Optional.of("whatever"); - config.rawValueKeys = keyValues("greeting/message", "greeting/name"); + void testRawKeysWithPrefix() { + ConsulConfig config = mock(ConsulConfig.class); + when(config.rawValueKeys()).thenReturn(keyValues("greeting/message", "greeting/name")); + when(config.prefix()).thenReturn(Optional.of("whatever")); + AgentConfig agentConfig = mock(AgentConfig.class); + when(config.agent()).thenReturn(agentConfig); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); when(mockGateway.getValue("whatever/greeting/message")) @@ -135,9 +133,7 @@ void testRawKeysWithPrefix() throws IOException { when(mockGateway.getValue("whatever/greeting/name")) .thenReturn(validResponse("whatever/greeting/name", "quarkus")); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - - Iterable configSources = sut.getConfigSources(null); + Iterable configSources = new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); assertThat(configSources).hasSize(2); assertThat(configSources).filteredOn(c -> c.getName().contains("message")).singleElement().satisfies(c -> { assertThat(c.getOrdinal()).isEqualTo(EXPECTED_ORDINAL); @@ -153,9 +149,11 @@ void testRawKeysWithPrefix() throws IOException { } @Test - void testPropertiesKeysWithoutPrefix() throws IOException { - ConsulConfig config = defaultConfig(); - config.propertiesValueKeys = keyValues("first", "second"); + void testPropertiesKeysWithoutPrefix() { + ConsulConfig config = mock(ConsulConfig.class); + when(config.propertiesValueKeys()).thenReturn(keyValues("first", "second")); + AgentConfig agentConfig = mock(AgentConfig.class); + when(config.agent()).thenReturn(agentConfig); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); when(mockGateway.getValue("first")) @@ -163,9 +161,7 @@ void testPropertiesKeysWithoutPrefix() throws IOException { when(mockGateway.getValue("second")) .thenReturn(validResponse("second", "other.key=value")); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - - Iterable configSources = sut.getConfigSources(null); + Iterable configSources = new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); assertThat(configSources).hasSize(2); assertThat(configSources).filteredOn(c -> c.getName().contains("first")).singleElement().satisfies(c -> { assertThat(c.getOrdinal()).isEqualTo(EXPECTED_ORDINAL); @@ -182,10 +178,12 @@ void testPropertiesKeysWithoutPrefix() throws IOException { } @Test - void testPropertiesKeysWithPrefix() throws IOException { - ConsulConfig config = defaultConfig(); - config.prefix = Optional.of("config"); - config.propertiesValueKeys = keyValues("first", "second"); + void testPropertiesKeysWithPrefix() { + ConsulConfig config = mock(ConsulConfig.class); + when(config.propertiesValueKeys()).thenReturn(keyValues("first", "second")); + when(config.prefix()).thenReturn(Optional.of("config")); + AgentConfig agentConfig = mock(AgentConfig.class); + when(config.agent()).thenReturn(agentConfig); ConsulConfigGateway mockGateway = mock(ConsulConfigGateway.class); when(mockGateway.getValue("config/first")) @@ -193,9 +191,7 @@ void testPropertiesKeysWithPrefix() throws IOException { when(mockGateway.getValue("config/second")) .thenReturn(validResponse("config/second", "other.key=value")); - ConsulConfigSourceProvider sut = new ConsulConfigSourceProvider(config, mockGateway); - - Iterable configSources = sut.getConfigSources(null); + Iterable configSources = new ConsulConfigSourceFactory().getConfigSources(config, mockGateway); assertThat(configSources).hasSize(2); assertThat(configSources).filteredOn(c -> c.getName().contains("first")).singleElement().satisfies(c -> { assertThat(c.getOrdinal()).isEqualTo(EXPECTED_ORDINAL); @@ -211,21 +207,7 @@ void testPropertiesKeysWithPrefix() throws IOException { verify(mockGateway, times(1)).getValue("config/second"); } - private ConsulConfig defaultConfig() { - ConsulConfig config = new ConsulConfig(); - config.enabled = true; - config.failOnMissingKey = true; - config.rawValueKeys = Optional.empty(); - config.propertiesValueKeys = Optional.empty(); - config.prefix = Optional.empty(); - config.agent = new ConsulConfig.AgentConfig(); - config.agent.readTimeout = Duration.ofSeconds(10); - config.agent.connectionTimeout = Duration.ofSeconds(10); - return config; - } - private Optional> keyValues(String... keys) { return Optional.of(Arrays.asList(keys)); } - } diff --git a/docs/modules/ROOT/pages/includes/attributes.adoc b/docs/modules/ROOT/pages/includes/attributes.adoc index 37df079..d7776c5 100644 --- a/docs/modules/ROOT/pages/includes/attributes.adoc +++ b/docs/modules/ROOT/pages/includes/attributes.adoc @@ -1,4 +1,4 @@ -:quarkus-version: 2.10.0.Final +:quarkus-version: 2.15.3.Final :quarkus-config-extensions-version: 1.1.0 :maven-version: 3.8.1+ diff --git a/docs/modules/ROOT/pages/includes/quarkus-consul-config.adoc b/docs/modules/ROOT/pages/includes/quarkus-consul-config.adoc index 1050af9..c16778d 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-consul-config.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-consul-config.adoc @@ -1,3 +1,5 @@ + +:summaryTableId: quarkus-consul-config [.configuration-legend] icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime [.configuration-reference.searchable, cols="80,.^10,.^10"] @@ -13,6 +15,13 @@ a| [[quarkus-consul-config_quarkus.consul-config.enabled]]`link:#quarkus-consul- [.description] -- If set to true, the application will attempt to look up the configuration from Consul + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_ENABLED+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_ENABLED+++` +endif::add-copy-button-to-env-var[] --|boolean |`false` @@ -21,8 +30,13 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.host-port]]`link:#quarkus [.description] -- -Consul agent host ---|host:port +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_HOST_PORT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_HOST_PORT+++` +endif::add-copy-button-to-env-var[] +--|InetSocketAddress |`localhost:8500` @@ -30,7 +44,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.use-https]]`link:#quarkus [.description] -- -Whether or not to use HTTPS when communicating with the agent +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_USE_HTTPS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_USE_HTTPS+++` +endif::add-copy-button-to-env-var[] --|boolean |`false` @@ -39,7 +58,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.token]]`link:#quarkus-con [.description] -- -Consul token to be provided when authentication is enabled +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_TOKEN+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_TOKEN+++` +endif::add-copy-button-to-env-var[] --|string | @@ -48,7 +72,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.trust-store]]`link:#quark [.description] -- -TrustStore to be used containing the SSL certificate used by Consul agent Can be either a classpath resource or a file system path +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_TRUST_STORE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_TRUST_STORE+++` +endif::add-copy-button-to-env-var[] --|path | @@ -57,7 +86,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.trust-store-password]]`li [.description] -- -Password of TrustStore to be used containing the SSL certificate used by Consul agent +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_TRUST_STORE_PASSWORD+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_TRUST_STORE_PASSWORD+++` +endif::add-copy-button-to-env-var[] --|string | @@ -66,7 +100,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.key-store]]`link:#quarkus [.description] -- -KeyStore to be used containing the SSL certificate for authentication with Consul agent Can be either a classpath resource or a file system path +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_KEY_STORE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_KEY_STORE+++` +endif::add-copy-button-to-env-var[] --|path | @@ -75,7 +114,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.key-store-password]]`link [.description] -- -Password of KeyStore to be used containing the SSL certificate for authentication with Consul agent +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_KEY_STORE_PASSWORD+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_KEY_STORE_PASSWORD+++` +endif::add-copy-button-to-env-var[] --|string | @@ -84,7 +128,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.key-password]]`link:#quar [.description] -- -Password to recover key from KeyStore for SSL client authentication with Consul agent If no value is provided, the key-store-password will be used +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_KEY_PASSWORD+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_KEY_PASSWORD+++` +endif::add-copy-button-to-env-var[] --|string | @@ -93,7 +142,12 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.trust-certs]]`link:#quark [.description] -- -When using HTTPS and no keyStore has been specified, whether or not to trust all certificates +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_TRUST_CERTS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_TRUST_CERTS+++` +endif::add-copy-button-to-env-var[] --|boolean |`false` @@ -102,22 +156,28 @@ a| [[quarkus-consul-config_quarkus.consul-config.agent.connection-timeout]]`link [.description] -- -The amount of time to wait when initially establishing a connection before giving up and timing out. - Specify `0` to wait indefinitely. ---|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration] - link:#duration-note-anchor[icon:question-circle[], title=More information about the Duration format] -|`10S` +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_CONNECTION_TIMEOUT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_CONNECTION_TIMEOUT+++` +endif::add-copy-button-to-env-var[] +--|Duration +|`10s` a| [[quarkus-consul-config_quarkus.consul-config.agent.read-timeout]]`link:#quarkus-consul-config_quarkus.consul-config.agent.read-timeout[quarkus.consul-config.agent.read-timeout]` [.description] -- -The amount of time to wait for a read on a socket before an exception is thrown. - Specify `0` to wait indefinitely. ---|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration] - link:#duration-note-anchor[icon:question-circle[], title=More information about the Duration format] -|`60S` +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_AGENT_READ_TIMEOUT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_AGENT_READ_TIMEOUT+++` +endif::add-copy-button-to-env-var[] +--|Duration +|`60s` a| [[quarkus-consul-config_quarkus.consul-config.prefix]]`link:#quarkus-consul-config_quarkus.consul-config.prefix[quarkus.consul-config.prefix]` @@ -125,6 +185,13 @@ a| [[quarkus-consul-config_quarkus.consul-config.prefix]]`link:#quarkus-consul-c [.description] -- Common prefix that all keys share when looking up the keys from Consul. The prefix is *not* included in the keys of the user configuration + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_PREFIX+++` +endif::add-copy-button-to-env-var[] --|string | @@ -134,6 +201,13 @@ a| [[quarkus-consul-config_quarkus.consul-config.raw-value-keys]]`link:#quarkus- [.description] -- Keys whose value is a raw string. When this is used, the keys that end up in the user configuration are the keys specified her with '/' replaced by '.' + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_RAW_VALUE_KEYS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_RAW_VALUE_KEYS+++` +endif::add-copy-button-to-env-var[] --|list of string | @@ -143,6 +217,13 @@ a| [[quarkus-consul-config_quarkus.consul-config.properties-value-keys]]`link:#q [.description] -- Keys whose value represents a properties file. When this is used, the keys that end up in the user configuration are the keys of the properties file, not these keys + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_PROPERTIES_VALUE_KEYS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_PROPERTIES_VALUE_KEYS+++` +endif::add-copy-button-to-env-var[] --|list of string | @@ -152,20 +233,14 @@ a| [[quarkus-consul-config_quarkus.consul-config.fail-on-missing-key]]`link:#qua [.description] -- If set to true, the application will not start if any of the configured config sources cannot be located + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CONSUL_CONFIG_FAIL_ON_MISSING_KEY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CONSUL_CONFIG_FAIL_ON_MISSING_KEY+++` +endif::add-copy-button-to-env-var[] --|boolean |`true` -|=== -ifndef::no-duration-note[] -[NOTE] -[[duration-note-anchor]] -.About the Duration format -==== -The format for durations uses the standard `java.time.Duration` format. -You can learn more about it in the link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-[Duration#parse() javadoc]. - -You can also provide duration values starting with a number. -In this case, if the value consists only of a number, the converter treats the value as seconds. -Otherwise, `PT` is implicitly prepended to the value to obtain a standard `java.time.Duration` format. -==== -endif::no-duration-note[] +|=== \ No newline at end of file diff --git a/jdbc/deployment/src/main/java/io/quarkiverse/config/jdbc/deployment/ConfigExtensionsProcessor.java b/jdbc/deployment/src/main/java/io/quarkiverse/config/jdbc/deployment/ConfigExtensionsProcessor.java index 28fe65f..b6e3808 100644 --- a/jdbc/deployment/src/main/java/io/quarkiverse/config/jdbc/deployment/ConfigExtensionsProcessor.java +++ b/jdbc/deployment/src/main/java/io/quarkiverse/config/jdbc/deployment/ConfigExtensionsProcessor.java @@ -1,6 +1,5 @@ package io.quarkiverse.config.jdbc.deployment; -import io.quarkiverse.config.jdbc.runtime.JdbcConfigConfig; import io.quarkiverse.config.jdbc.runtime.JdbcConfigSourceFactoryBuilder; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -18,7 +17,7 @@ FeatureBuildItem feature() { } @BuildStep - void jdbcConfigFactory(JdbcConfigConfig config, + void jdbcConfigFactory( BuildProducer staticInitConfigBuilder, BuildProducer runTimeConfigBuilder) { staticInitConfigBuilder.produce(new StaticInitConfigBuilderBuildItem(JdbcConfigSourceFactoryBuilder.class.getName())); diff --git a/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigConfig.java b/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigConfig.java index d95994b..b8ba43c 100644 --- a/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigConfig.java +++ b/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigConfig.java @@ -1,93 +1,100 @@ package io.quarkiverse.config.jdbc.runtime; import java.time.Duration; -import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.runtime.configuration.DurationConverter; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithConverter; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; /** * We don't really use this, because these are configurations for the config itself, so it causes a chicken / egg * problem, but we have it so the configurations can be properly documented. - * + *
* The config itself is loaded using the ConfigSourceContext on the ConfigSourceFactory */ -@ConfigRoot(name = "config.source.jdbc", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) -public class JdbcConfigConfig { +@ConfigMapping(prefix = "quarkus.config.source.jdbc") +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public interface JdbcConfigConfig { /** * If set to true, the application will attempt to look up the configuration from DB */ - @ConfigItem(name = "enabled", defaultValue = "true") - public boolean enabled = true; + @WithDefault("true") + boolean enabled(); /** * If set to true, the application will cache all looked up the configuration from DB in memory * If set to false, the application will always get the latest values from the DB */ - @ConfigItem(name = "cache", defaultValue = "true") - public boolean cache = true; + @WithDefault("true") + boolean cache(); /** * Table name for configuration records */ - @ConfigItem(name = "table", defaultValue = "configuration") - public String table; + @WithDefault("configuration") + String table(); /** * Name of the column containing the key */ - @ConfigItem(name = "key", defaultValue = "configuration_key") - public String keyColumn; + @WithName("key") + @WithDefault("configuration_key") + String keyColumn(); /** * Name of the column containing the value */ - @ConfigItem(name = "value", defaultValue = "configuration_value") - public String valueColumn; + @WithName("value") + @WithDefault("configuration_value") + String valueColumn(); /** * The datasource username, if not defined the username of the default datasource is used */ - @ConfigItem(name = "username") - public Optional username = Optional.empty();; + @WithDefault("${quarkus.datasource.username}") + String username(); /** * The datasource password, if not defined the password of the default datasource is used */ - @ConfigItem(name = "password") - public Optional password = Optional.empty();; + @WithDefault("${quarkus.datasource.password}") + String password(); /** * The datasource URL, if not defined the URL of the default datasource is used */ - @ConfigItem(name = "url") - public Optional url = Optional.empty(); + @WithDefault("${quarkus.datasource.jdbc.url}") + String url(); /** * The initial size of the pool. Usually you will want to set the initial size to match at least the * minimal size, but this is not enforced so to allow for architectures which prefer a lazy initialization * of the connections on boot, while being able to sustain a minimal pool size after boot. */ - @ConfigItem(name = "initial-size", defaultValue = "0") - public int initialSize = 0; + @WithDefault("0") + int initialSize(); /** * The datasource pool minimum size */ - @ConfigItem(name = "min-size", defaultValue = "0") - public int minSize = 0; + @WithDefault("0") + int minSize(); /** * The datasource pool maximum size */ - @ConfigItem(name = "max-size", defaultValue = "5") - public int maxSize = 5; + @WithDefault("5") + int maxSize(); /** * The timeout before cancelling the acquisition of a new connection */ - @ConfigItem(name = "acquisition-timeout", defaultValue = "5") - public Duration acquisitionTimeout = Duration.ofSeconds(5); + @WithDefault("5") + @WithConverter(DurationConverter.class) + Duration acquisitionTimeout(); } diff --git a/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactory.java b/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactory.java index bf7b6af..e510902 100644 --- a/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactory.java +++ b/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactory.java @@ -1,12 +1,8 @@ package io.quarkiverse.config.jdbc.runtime; import java.sql.SQLException; -import java.time.Duration; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Map; -import java.util.Optional; import org.eclipse.microprofile.config.spi.ConfigSource; import org.jboss.logging.Logger; @@ -15,94 +11,26 @@ import io.smallrye.config.ConfigSourceFactory; import io.smallrye.config.common.MapBackedConfigSource; -public class JdbcConfigSourceFactory implements ConfigSourceFactory { - +public class JdbcConfigSourceFactory implements ConfigSourceFactory.ConfigurableConfigSourceFactory { private static final Logger log = Logger.getLogger(JdbcConfigSourceFactory.class); @Override - public Iterable getConfigSources(ConfigSourceContext context) { - try { - final JdbcConfigConfig config = populateConfig(context); - final Repository repository = new Repository(config); - return getConfigSource(config, repository); - } catch (SQLException e) { - log.warn("jdbc-config disabled. reason: " + e.getLocalizedMessage()); + public Iterable getConfigSources(final ConfigSourceContext context, final JdbcConfigConfig config) { + if (!config.enabled()) { return Collections.emptyList(); } - } - protected List getConfigSource(final JdbcConfigConfig config, final Repository repository) { - if (!config.enabled) { + try { + Repository repository = new Repository(config); + if (config.cache()) { + return Collections.singletonList(new InMemoryConfigSource("jdbc-config", repository.getAllConfigValues(), 400)); + } else { + return Collections.singletonList(new JdbcConfigSource("jdbc-config", repository, 400)); + } + } catch (SQLException e) { + log.warn("jdbc-config disabled. reason: " + e.getLocalizedMessage()); return Collections.emptyList(); } - - final List list = new ArrayList<>(); - final Map result = repository.getAllConfigValues(); - - if (config.cache) { - list.add(new InMemoryConfigSource("jdbc-config", result, 400)); - } else { - list.add(new JdbcConfigSource("jdbc-config", repository, 400)); - } - - return list; - } - - private JdbcConfigConfig populateConfig(ConfigSourceContext context) { - final JdbcConfigConfig config = new JdbcConfigConfig(); - - // jdbc-config parameters - config.enabled = Boolean - .parseBoolean(Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.enabled").getValue()) - .orElse(String.valueOf(config.enabled))); - - // short-circuit if config is disabled - if (!config.enabled) { - return config; - } - - config.cache = Boolean.parseBoolean(Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.cache").getValue()) - .orElse(String.valueOf(config.cache))); - - // table, keyColumn, valueColumn - config.table = Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.table").getValue()) - .orElse(config.table); - config.keyColumn = Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.key").getValue()) - .orElse(config.keyColumn); - config.valueColumn = Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.value").getValue()) - .orElse(config.valueColumn); - - // connection parameters - - // url, username, password (use default datasource values if jdbc-config values are not defined ) - config.username = Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.username").getValue()) - .or(() -> Optional.ofNullable(context.getValue("quarkus.datasource.username").getValue())); - config.password = Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.password").getValue()) - .or(() -> Optional.ofNullable(context.getValue("quarkus.datasource.password").getValue())); - config.url = Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.url").getValue()) - .or(() -> Optional.ofNullable(context.getValue("quarkus.datasource.jdbc.url").getValue())); - - // initialSize, minSize, maxSize, acquisitionTimeout - config.initialSize = Integer - .parseInt(Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.initial-size").getValue()) - .orElse(String.valueOf(config.initialSize))); - config.minSize = Integer - .parseInt(Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.min-size").getValue()) - .orElse(String.valueOf(config.minSize))); - config.maxSize = Integer - .parseInt(Optional.ofNullable(context.getValue("quarkus.config.source.jdbc.max-size").getValue()) - .orElse(String.valueOf(config.maxSize))); - - Optional timeout = Optional - .ofNullable(context.getValue("quarkus.config.source.jdbc.acquisition-timeout").getValue()); - - // acquisitionTimeout (java.time.Duration) is allowed to be just numbers (seconds) or standard Duration format (PTXX) - if (timeout.isPresent() && timeout.get().matches("[0-9]+")) { - config.acquisitionTimeout = Duration.ofSeconds(Long.parseLong(timeout.get())); - } else { - config.acquisitionTimeout = Duration.parse(timeout.orElse(config.acquisitionTimeout.toString())); - } - return config; } private static final class InMemoryConfigSource extends MapBackedConfigSource { diff --git a/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/Repository.java b/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/Repository.java index 4e3aa48..2c75e5e 100644 --- a/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/Repository.java +++ b/jdbc/runtime/src/main/java/io/quarkiverse/config/jdbc/runtime/Repository.java @@ -93,25 +93,26 @@ private void prepareDataSource(final JdbcConfigConfig config) throws SQLExceptio // configure pool poolConfiguration - .initialSize(config.initialSize) - .minSize(config.initialSize) - .maxSize(config.maxSize) - .acquisitionTimeout(config.acquisitionTimeout); + .initialSize(config.initialSize()) + .minSize(config.minSize()) + .maxSize(config.maxSize()) + .acquisitionTimeout(config.acquisitionTimeout()); // configure supplier connectionFactoryConfiguration - .jdbcUrl(config.url.get()) - .credential(new NamePrincipal(config.username.get())) - .credential(new SimplePassword(config.password.get())); + .jdbcUrl(config.url()) + .credential(new NamePrincipal(config.username())) + .credential(new SimplePassword(config.password())); dataSource = AgroalDataSource.from(dataSourceConfiguration.get()); } private void prepareQueries(final JdbcConfigConfig config) { - selectAllQuery = "SELECT conf." + config.keyColumn + ", conf." + config.valueColumn + " FROM " + config.table + " conf"; - selectKeysQuery = "SELECT conf." + config.keyColumn + " FROM " + config.table + " conf"; - selectValueQuery = "SELECT conf." + config.valueColumn + " FROM " + config.table + " conf WHERE conf." - + config.keyColumn + " = ?1"; + selectAllQuery = "SELECT conf." + config.keyColumn() + ", conf." + config.valueColumn() + " FROM " + config.table() + + " conf"; + selectKeysQuery = "SELECT conf." + config.keyColumn() + " FROM " + config.table() + " conf"; + selectValueQuery = "SELECT conf." + config.valueColumn() + " FROM " + config.table() + " conf WHERE conf." + + config.keyColumn() + " = ?1"; } @Override diff --git a/jdbc/runtime/src/test/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactoryTest.java b/jdbc/runtime/src/test/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactoryTest.java deleted file mode 100644 index 901d93b..0000000 --- a/jdbc/runtime/src/test/java/io/quarkiverse/config/jdbc/runtime/JdbcConfigSourceFactoryTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.quarkiverse.config.jdbc.runtime; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.microprofile.config.spi.ConfigSource; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class JdbcConfigSourceFactoryTest { - private final JdbcConfigConfig config = mock(JdbcConfigConfig.class); - private final Repository repository = mock(Repository.class); - - @Test - @DisplayName("Repository returns data") - void testOnStoredData() throws SQLException { - String key = "foo"; - String value = "sample value"; - JdbcConfigSourceFactory factory = new JdbcConfigSourceFactory(); - Map map = new HashMap<>(); - map.put(key, value); - - config.enabled = true; - - when(repository.getAllConfigValues()).thenReturn(map); - when(repository.getValue(key)).thenReturn(value); - - List configSources = factory.getConfigSource(config, repository); - assertEquals(1, configSources.size()); - - ConfigSource configSource = configSources.iterator().next(); - assertEquals(value, configSource.getValue(key)); - - } - - @Test - @DisplayName("On disabled datasource is empty") - void testDisabledJdbcConfig() { - JdbcConfigSourceFactory factory = new JdbcConfigSourceFactory(); - - config.enabled = false; - - List configSources = factory.getConfigSource(config, repository); - assertTrue(configSources.isEmpty()); - - } - -}