Skip to content

Commit

Permalink
Clarification and test for using converters and injected config
Browse files Browse the repository at this point in the history
Registered converters are applied in getValue() methods. Default config and injected config are equal (plus test). Default config registers discovered converters.
Fixes #348.
Signed-off-by:Ondrej Mihalyi <ondrej.mihalyi@payara.fish>
  • Loading branch information
Ondrej Mihalyi committed Apr 20, 2018
1 parent fd1b7ec commit 796d70a
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 13 deletions.
8 changes: 8 additions & 0 deletions api/src/main/java/org/eclipse/microprofile/config/Config.java
Expand Up @@ -46,6 +46,10 @@
* <p>
* The config objects produced via the injection model <pre>@Inject Config</pre> are guaranteed to be serializable, while
* the programmatically created ones are not required to be serializable.
* <p>
* If one or more converters are registered for a class of a requested value then one of the registered converters
* is used to convert the string value retrieved from config sources.
* For more information about converters, see {@link org.eclipse.microprofile.config.spi.Converter}
*
* <h3>Usage</h3>
*
Expand Down Expand Up @@ -85,6 +89,8 @@ public interface Config {
/**
* Return the resolved property value with the specified type for the
* specified property name from the underlying {@link ConfigSource ConfigSources}.
*
* Registered converters are applied if they match the propertyType class
*
* If this method gets used very often then consider to locally store the configured value.
*
Expand All @@ -104,6 +110,8 @@ public interface Config {
* Return the resolved property value with the specified type for the
* specified property name from the underlying {@link ConfigSource ConfigSources}.
*
* Registered converters are applied if they match the propertyType class
*
* If this method is used very often then consider to locally store the configured value.
*
* @param <T>
Expand Down
Expand Up @@ -79,6 +79,10 @@ private ConfigProvider() {
/**
* Provide a {@link Config} based on all {@link org.eclipse.microprofile.config.spi.ConfigSource ConfigSources} of the
* current Thread Context ClassLoader (TCCL)
* <p>
*
* <p>
*
* The {@link Config} will be stored for future retrieval.
* <p>
* There is exactly a single Config instance per ClassLoader
Expand Down
Expand Up @@ -32,9 +32,13 @@
import javax.inject.Qualifier;

/**
* <p>
* Binds the injection point with a configured value.
* Can be used to annotate injection points of type {@code TYPE}, {@code Optional<TYPE>} or {@code javax.inject.Provider<TYPE>},
* where {@code TYPE} can be {@code String} and all types which have appropriate converters.
* <p>
* Injected values are the same values that would be retrieved from an injected {@link org.eclipse.microprofile.config.Config} instance
* or from the instance retrieved from {@link org.eclipse.microprofile.config.ConfigProvider#getConfig()}
*
* <h2>Examples</h2>
*
Expand Down
10 changes: 7 additions & 3 deletions spec/src/main/asciidoc/configprovider.asciidoc
Expand Up @@ -26,23 +26,27 @@ For using Microprofile-Config in a programmatic way the `ConfigProvider` class i
It allows access to different configurations (represented by a `Config` instance) based on the application in which it is used.
The `ConfigProvider` internally delegates through to the `ConfigProviderResolver` which contains more low-level functionality.

There are 4 different ways to create an `Config` instance:
There are 4 different ways to create a `Config` instance:

* In CDI managed components a user can use `@Inject` to access the current application configuration.
* In CDI managed components, a user can use `@Inject` to access the current application configuration.
The default and the auto discovered <<configsource,ConfigSources>> will be gathered to form a configuration.
The default and the auto discovered <<converters,Converters>> will be gathered to form a configuration.
Injected instance of `Config` should behave the same as the one retrieved by `ConfigProvider.getConfig()`. Injected values should be the same as if retrieved from the injected `Config` instance.

* A factory method `ConfigProvider#getConfig()` to create a `Config` object based on automatically picked up `ConfigSources`
of the Application identified by the current Thread Context ClassLoader classpath.
The default and the auto discovered <<converters,Converters>> will be gathered to form a configuration.
Subsequent calls to this method for a certain Application will return the same `Config` instance.

* A factory method `ConfigProvider#getConfig(ClassLoader forClassLoader)` to create a `Config` object based on automatically picked up `ConfigSources`
of the Application identified by the given ClassLoader.
The default and the auto discovered <<converters,Converters>> will be gathered to form a configuration.
This can be used if the Thread Context ClassLoader does not represent the correct layer.
E.g. if you need the Config for a class in a shared EAR lib folder.
Subsequent calls to this method for a certain Application will return the same `Config` instance.

* A factory method `ConfigProviderResolver#getBuilder()` to create a `ConfigBuilder` object.
The builder has no config sources but with only the default converters added. The `ConfigBuilder` object can be filled manually via `ConfigBuilder#withSources(ConfigSources... sources)`.
The builder has no config sources. Only the default converters are added. The `ConfigBuilder` object can be filled manually via methods like `ConfigBuilder#withSources(ConfigSources... sources)`.
This configuration instance will by default not be shared by the `ConfigProvider`.
This method is intended be used if a IoC container or any other external Factory can be used to give access to a manually created shared `Config`.

Expand Down
6 changes: 6 additions & 0 deletions tck/pom.xml
Expand Up @@ -83,6 +83,12 @@
<artifactId>testng</artifactId>
<version>6.9.9</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.testng</groupId>
Expand Down
Expand Up @@ -32,6 +32,7 @@
import javax.enterprise.inject.spi.CDI;
import javax.inject.Inject;
import javax.inject.Provider;
import org.eclipse.microprofile.config.ConfigProvider;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.config.spi.ConfigSource;
Expand Down Expand Up @@ -64,8 +65,8 @@ public static Archive deployment() {
}

@Test
public void can_inject_simple_values_when_defined() {
ensure_all_property_values_are_defined();
public void canInjectSimpleValuesWhenDefined() {
ensureAllPropertyValuesAreDefined();

SimpleValuesBean bean = getBeanOfType(SimpleValuesBean.class);

Expand All @@ -85,31 +86,58 @@ public void can_inject_simple_values_when_defined() {
assertThat(bean.doublePropertyWithDefaultValue, is(closeTo(3.1415, 0.1)));
}

/*
* Refers to chapter "Accessing or Creating a certain Configuration"
*/
@Test
public void can_inject_dynamic_values_via_CDI_provider() {
clear_all_property_values();
public void injectedValuesAreEqualToProgrammaticValues() {
ensureAllPropertyValuesAreDefined();

SimpleValuesBean bean = getBeanOfType(SimpleValuesBean.class);

assertThat(bean.stringProperty, is(equalTo(
ConfigProvider.getConfig().getValue("my.string.property", String.class))));
assertThat(bean.booleanProperty, is(equalTo(
ConfigProvider.getConfig().getValue("my.boolean.property", Boolean.class))));
assertThat(bean.intProperty, is(equalTo(
ConfigProvider.getConfig().getValue("my.int.property", Integer.class))));
assertThat(bean.longProperty, is(equalTo(
ConfigProvider.getConfig().getValue("my.long.property", Long.class))));
assertThat(bean.floatProperty, is(floatCloseTo(
ConfigProvider.getConfig().getValue("my.float.property", Float.class), 0.1f)));
assertThat(bean.doubleProperty, is(closeTo(
ConfigProvider.getConfig().getValue("my.double.property", Double.class), 0.1)));

assertThat(bean.doublePropertyWithDefaultValue, is(closeTo(
ConfigProvider.getConfig().getOptionalValue("my.not.configured.double.property", Double.class)
.orElse(3.1415), 0.1)));
}

@Test
public void canInjectDynamicValuesViaCdiProvider() {
clearAllPropertyValues();

DynamicValuesBean bean = getBeanOfType(DynamicValuesBean.class);

//X TODO clarify how Provider<T> should behave for missing values assertThat(bean.getIntProperty(), is(nullValue()));

ensure_all_property_values_are_defined();
ensureAllPropertyValuesAreDefined();

assertThat(bean.getIntProperty(), is(equalTo(5)));
}

@Test
public void can_inject_default_property_path() {
clear_all_property_values();
public void canInjectDefaultPropertyPath() {
clearAllPropertyValues();

ensure_all_property_values_are_defined();
ensureAllPropertyValuesAreDefined();

DefaultPropertyBean bean = getBeanOfType(DefaultPropertyBean.class);

assertThat(bean.getConfigProperty(), is(equalTo("pathConfigValue")));
}

private void ensure_all_property_values_are_defined() {
private void ensureAllPropertyValuesAreDefined() {
System.setProperty("my.string.property", "text");
System.setProperty("my.boolean.property", "true");
System.setProperty("my.int.property", "5");
Expand All @@ -119,7 +147,7 @@ private void ensure_all_property_values_are_defined() {
System.setProperty(DEFAULT_PROPERTY_BEAN_KEY, "pathConfigValue");
}

private void clear_all_property_values() {
private void clearAllPropertyValues() {
System.getProperties().remove("my.string.property");
System.getProperties().remove("my.boolean.property");
System.getProperties().remove("my.int.property");
Expand Down
Expand Up @@ -53,6 +53,11 @@ public boolean matches(Object item) {
public void describeTo(Description description) {
doubleMatcher.describeTo(description);
}

@Override
public void describeMismatch(Object item, Description mismatchDescription) {
doubleMatcher.describeMismatch(item, mismatchDescription);
}
};
}

Expand Down

0 comments on commit 796d70a

Please sign in to comment.