From 535aca922394b69599726db0db8e85fc21347cea Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Mon, 18 May 2020 20:03:22 +0100 Subject: [PATCH] ConfigValue API. Signed-off-by: Roberto Cortez --- .../eclipse/microprofile/config/Config.java | 22 ++- .../microprofile/config/ConfigValue.java | 64 ++++++++ .../src/main/asciidoc/configexamples.asciidoc | 12 +- .../config/tck/ConfigValueTest.java | 145 ++++++++++++++++++ 4 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 api/src/main/java/org/eclipse/microprofile/config/ConfigValue.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/config/tck/ConfigValueTest.java diff --git a/api/src/main/java/org/eclipse/microprofile/config/Config.java b/api/src/main/java/org/eclipse/microprofile/config/Config.java index 3f717426..8e8b2fcc 100644 --- a/api/src/main/java/org/eclipse/microprofile/config/Config.java +++ b/api/src/main/java/org/eclipse/microprofile/config/Config.java @@ -90,7 +90,6 @@ */ @org.osgi.annotation.versioning.ProviderType public interface Config { - /** * Return the resolved property value with the specified type for the * specified property name from the underlying {@linkplain ConfigSource configuration sources}. @@ -111,6 +110,27 @@ public interface Config { */ T getValue(String propertyName, Class propertyType); + /** + * Return the {@link ConfigValue} for the specified property name from the underlying + * {@linkplain ConfigSource configuration source}. The lookup of the configuration is performed immediatily, + * meaning that calls to {@link ConfigValue} will always yeld the same results. + *

+ * + * The configuration value is not guaranteed to be cached by the implementation, and may be expensive + * to compute; therefore, if the returned value is intended to be frequently used, callers should consider storing + * rather than recomputing it. + *

+ * + * A {@link ConfigValue} is always returned even if a property name cannot be found. In this case, every method in + * {@link ConfigValue} returns {@code null} except for {@link ConfigValue#getName()}, which includes the original + * property name being looked up. + * + * @param propertyName + * The configuration property name + * @return the resolved property value as a {@link ConfigValue} + */ + ConfigValue getConfigValue(String propertyName); + /** * Return the resolved property values with the specified type for the * specified property name from the underlying {@linkplain ConfigSource configuration sources}. diff --git a/api/src/main/java/org/eclipse/microprofile/config/ConfigValue.java b/api/src/main/java/org/eclipse/microprofile/config/ConfigValue.java new file mode 100644 index 00000000..ed249f39 --- /dev/null +++ b/api/src/main/java/org/eclipse/microprofile/config/ConfigValue.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.config; + +/** + * The ConfigValue holds additional information after the lookup of a configuration property and is itself immutable. + *

+ * + * Holds information about the configuration property name, configuration value, the + * {@link org.eclipse.microprofile.config.spi.ConfigSource} name from where the configuration property was loaded and + * the ordinal of the {@link org.eclipse.microprofile.config.spi.ConfigSource}. + *

+ * + * This is used together with {@link Config} to expose the configuration property lookup metadata. + * + * @author Roberto Cortez + */ +public interface ConfigValue { + /** + * The name of the property. + * + * @return the name of the property. + */ + String getName(); + + /** + * The value of the property lookup. + * + * @return the raw value of the property lookup or {@code null} if the property could not be found. + */ + String getValue(); + + /** + * The {@link org.eclipse.microprofile.config.spi.ConfigSource} name that loaded the property lookup. + * + * @return the ConfigSource name that loaded the property lookup or {@code null} if the property could not be found. + */ + String getSourceName(); + + /** + * The {@link org.eclipse.microprofile.config.spi.ConfigSource} ordinal that loaded the property lookup. + * + * @return the ConfigSource ordinal that loaded the property lookup or {@code 0} if the property could not be + * found. + */ + int getSourceOrdinal(); +} diff --git a/spec/src/main/asciidoc/configexamples.asciidoc b/spec/src/main/asciidoc/configexamples.asciidoc index 14bc89a2..22a3443e 100644 --- a/spec/src/main/asciidoc/configexamples.asciidoc +++ b/spec/src/main/asciidoc/configexamples.asciidoc @@ -38,8 +38,11 @@ public class ConfigUsageSample { Config config = ConfigProvider.getConfig(); String serverUrl = config.getValue("acme.myprj.some.url", String.class); - callToServer(serverUrl); + + // or + ConfigValue configServerUrl = config.getConfigValue("acme.myprj.some.url"); + callToServer(configServerUrl.getValue()); } } ---- @@ -81,6 +84,13 @@ public class InjectedConfigUsageSample { @ConfigProperty(name="myprj.some.url") private String someUrl; + // You can also inject a configuration using the ConfigValue metadata object. The + // configured value will not lead to a DeploymentException if the value is missing. + // A default value can also be specified like any other configuration. + @Inject + @ConfigProperty(name="myprj.another.url") + private ConfigValue anotherUrl; + //The following code injects an Optional value of myprj.some.port property. //Contrary to natively injecting the configured value, this will not lead to a //DeploymentException if the value is missing. diff --git a/tck/src/main/java/org/eclipse/microprofile/config/tck/ConfigValueTest.java b/tck/src/main/java/org/eclipse/microprofile/config/tck/ConfigValueTest.java new file mode 100644 index 00000000..25d15d47 --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/config/tck/ConfigValueTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.eclipse.microprofile.config.tck; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.ConfigValue; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.testng.annotations.Test; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; + +public class ConfigValueTest extends Arquillian { + @Deployment + public static Archive deployment() { + JavaArchive testJar = ShrinkWrap + .create(JavaArchive.class, "ConfigValueTest.jar") + .addClasses(ConfigValueBean.class) + .addAsServiceProvider(ConfigSource.class, ConfigValueConfigSource.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml") + .as(JavaArchive.class); + + return ShrinkWrap + .create(WebArchive.class, "ConfigValueTest.war") + .addAsLibrary(testJar); + } + + @Test + void configValue() { + ConfigValue configValue = ConfigProvider.getConfig().getConfigValue("my.prop"); + assertNotNull(configValue); + assertEquals("my.prop", configValue.getName()); + assertEquals("1234", configValue.getValue()); + assertEquals("ConfigValueConfigSource", configValue.getSourceName()); + assertEquals(1000, configValue.getSourceOrdinal()); + } + + @Test + public void configValueEmpty() { + ConfigValue configValue = ConfigProvider.getConfig().getConfigValue("not.found"); + assertNotNull(configValue); + assertEquals("not.found", configValue.getName()); + assertNull(configValue.getValue()); + assertNull(configValue.getSourceName()); + assertEquals(0, configValue.getSourceOrdinal()); + } + + @Inject + private ConfigValueBean configValueBean; + + @Test + public void configValueInjection() { + final ConfigValue configValue = configValueBean.getConfigValue(); + assertNotNull(configValue); + assertEquals("my.prop", configValue.getName()); + assertEquals("1234", configValue.getValue()); + assertEquals(ConfigValueConfigSource.class.getSimpleName(), configValue.getSourceName()); + assertEquals(1000, configValue.getSourceOrdinal()); + + final ConfigValue configValueDefault = configValueBean.getConfigValueDefault(); + assertNotNull(configValueDefault); + assertEquals("my.prop", configValue.getName()); + assertEquals("default", configValueDefault.getValue()); + assertNull(configValueDefault.getSourceName()); + } + + public static class ConfigValueConfigSource implements ConfigSource { + private Map properties; + + public ConfigValueConfigSource() { + properties = new HashMap<>(); + properties.put("my.prop", "1234"); + } + + @Override + public Set getPropertyNames() { + return properties.keySet(); + } + + @Override + public String getValue(final String propertyName) { + return properties.get(propertyName); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + @Override + public int getOrdinal() { + return 1000; + } + } + + @Dependent + public static class ConfigValueBean { + @Inject + @ConfigProperty(name = "my.prop") + private ConfigValue configValue; + + @Inject + @ConfigProperty(name = "my.prop.default", defaultValue = "default") + private ConfigValue configValueDefault; + + ConfigValue getConfigValue() { + return configValue; + } + + ConfigValue getConfigValueDefault() { + return configValueDefault; + } + } +}