Skip to content

Commit

Permalink
240 config properties
Browse files Browse the repository at this point in the history
Signed-off-by: Emily Jiang <[email protected]>
  • Loading branch information
Emily-Jiang committed Jul 1, 2020
1 parent 24b76c9 commit 8127e34
Show file tree
Hide file tree
Showing 10 changed files with 1,029 additions and 1 deletion.
45 changes: 45 additions & 0 deletions api/src/main/java/org/eclipse/microprofile/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,51 @@ default <T> Optional<List<T>> getOptionalValues(String propertyName, Class<T> pr
return getOptionalValue(propertyName, arrayType).map(Arrays::asList);
}

/**
* Return the resolved configuration properties instance with the specified prefix.
* The type declaration can be annotated with
* {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}.
* If the type is annotated with {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties},
* the prefix supplied in this method overrides the prefix associated with the annotation
* {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}.
*
* @param <T>
* The Class Type
* @param configProperties
* The class that contains a number of fields that maps to corresponding configuration properties.
* The type declaration can be annotated with
* {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}.
* The prefix as the method parameter overrides the prefix set on the type.
* This class must contain a zero-arg constructor.
* @param prefix
* The prefix for the configuration properties declared on the class configProperties.
* If the prefix is "", which means no prefix involved when performing property lookup.
* If the prefix is null, this method is equivalent to {@linkplain #getConfigProperties(Class)}.
* @return An instance for the specified type and prefix
*/
<T> T getConfigProperties(Class<T> configProperties, String prefix);

/**
* Return the resolved configuration properties instance. The type can be annotated with
* {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}.
* If the type is annotated with {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties},
* the prefix associated with the annotation {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties} will be used.
* @param <T>
* The Class Type
* @param configProperties
* The class that contains a number of fields that maps to corresponding configuration properties.
* The type declaration can be annotated with
* {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}.
* If the type is annotated with {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties},
* the prefix on the annotation {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}
* will be honoured.
* The absence of the annotation {@linkplain org.eclipse.microprofile.config.inject.ConfigProperties @ConfigProperties}
* or the absence of the prefix on the annotation means no prefix specified.
* This class must contain a zero-arg constructor.
* @return An instance for the specified type
*/
<T> T getConfigProperties(Class<T> configProperties);

/**
* Returns a sequence of configuration property names. The order of the returned property names is unspecified.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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.inject;


import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;

/**
* <p>
* Retrieve a number of related configuration properties with the specified prefix into a property class.
* <pre>
* &#064;ConfigProperties(prefix="server")
* public class MyServer {
* public String host; //maps the property name server.host
* public int port; //maps to the property name server.port
* private String context; //maps to the property name server.context
* public &#064;ConfigProperty(name="old.location")
* String location; //maps to the property name server.old.location
* public String getContext() {
* return context;
* }
* }
* </pre>
*
*
*
* @since 2.0
* @author <a href="mailto:[email protected]">Emily Jiang</a>
*
*/

@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
@Documented
@Qualifier
public @interface ConfigProperties {

/**
* The prefix of the configuration properties
* @return the configuration property prefix
*/
@Nonbinding
String prefix() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* @author <a href="mailto:[email protected]">Emily Jiang</a>
*
*/
@org.osgi.annotation.versioning.Version("1.0.1")

@org.osgi.annotation.versioning.Version("1.1")
package org.eclipse.microprofile.config.inject;

152 changes: 152 additions & 0 deletions spec/src/main/asciidoc/configexamples.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,155 @@ public class InjectedConfigUsageSample {
}
----

=== Aggregate related properties into a single POJO Example

When injecting a number of related configuration properties, it can be tedious to repeat the statement of `ConfigProperty` in scatter places.
Since they are related, it makes more sense to aggregate them into a single property class.

MicroProfile Config provides a way to look up a number of configuration properties starting with the same prefix using the `@ConfigProperties` annotation, e.g. `ConfigProperties(prefix="myPrefix")`.
When annotating a class with `@ConfigProperties` or `@ConfigProperties(prefix="myPrefix")`, any of its fields, regardless of the visibility, maps to a configuration property via the following mapping rules.

* If the `prefix` is present, the field `x` maps to the configuration property `<prefix>.x`.
* If the `prefix` is absent, the field `x` maps to the property name `x`.

If the field name `x` needs to be different from the config property name `y`, use `@ConfigProperty(name="y")` to perform the transformation.
If the prefix is present, the field `x` maps to the configuration property `<prefix>.y`, otherwise `y`.

Considering the following config sources:

[source, text]
----
config_ordinal = 120
server.host = localhost
server.port=9080
server.endpoint=query
server.old.location=London
----

[source, text]
----
config_ordinal = 150
client.host = myHost
client.port=9081
client.endpoint=shelf
client.old.location=Dublin
host = anotherHost
port=9082
endpoint=book
old.location=Berlin
----

In order to retrieve the above properties in a single property class, you can use the `@ConfigProperties` annotation with a prefix.

[source, java]
----
@ConfigProperties(prefix="server")
@ApplicationScoped
public class Details {
public String host; // the value of the configuration property server.host
public int port; // the value of the configuration property server.port
private String endpoint; //the value of the configuration property server.endpoint
public @ConfigProperty(name="old.location")
String location; //the value of the configuration property server.old.location
public String getEndpoint() {
return endpoint;
}
}
----

You can then use one of the following to retrieve the properties.

* Directly inject the bean annotated with `@ConfigProperties`
[source, java]
----
@Inject Details serverDetails;
----

The `serverDetails` will contain the following info, as the prefix is `server`:

```
serverDetails.host -> server.host -> localhost
serverDetails.port -> server.port -> 9080
serverDetails.endpoint -> server.endpoint -> query
serverDetails.getLocation() -> server.old.location -> London
```
* Specify `@ConfigProperties` when injecting the bean annotated with `@ConfigProperties`

In this case, the prefix associated with `@ConfigProperties` on this injection point overrides the prefix specified on the bean class.

----
@Inject @ConfigProperties(prefix="client") Details clientDetails;
----
The prefix `client` overrides the prefix `server` on the `ServerDetails` bean. Therefore, this will retrieve the following properties.

```
clientDetails.host -> client.host -> myHost
clientDetails.port -> client.port -> 9081
clientDetails.endpoint -> client.endpoint -> shelf
clientDetails.getLocation() -> client.old.location -> Dublin
```
If `@ConfigProperties` has no associated prefix, it means the prefix is empty. e.g.
----
@Inject @ConfigProperties Details details; //prefix is empty
----
The absence of the prefix means the prefix is empty, which overrides the prefix set on the bean class `server`. Therefore, this will retrieve the following properties.

```
details.host -> host -> anotherHost
details.port -> port -> 9082
details.endpoint -> endpoint -> book
details.getLocation() -> old.location -> Berlin
```

In the above two types of injection lookup, the configuration properties class must contain a zero-arg constructor. Otherwise, DeploymentException will be thrown.
If any of the property is missing and there is neither default value nor property is not optional, DeploymentException will be thrown.
In order to avoid this, you can supply a default value when defining the field. Alternatively, you can use `@ConfigProperty` to provide a default value.
You can also use `Optional<T>` or OptionalInt, OptionalDouble, OptionalLong as the type.
If any of the property value cannot be converted to the specified type, DeploymentException will be thrown as well.

* programmatic look up via `Config.getConfigProperties()`
[source, java]
----
Config config = ConfigProvider.getConfig();
Details serverDetails = config.getConfigProperties(Details.class);
Details clientDetails = config.getConfigProperties(Details.class, "client");
Details details = config.getConfigProperties(Details.class, ""); //use no prefix
----

In the above example, the `serverDetails` will contain the following info, because the prefix `server` on the bean class `Details` is in action:

```
serverDetails.host -> server.host -> localhost
serverDetails.port -> server.port -> 9080
serverDetails.endpoint -> server.endpoint -> query
serverDetails.getLocation() -> server.old.location -> London
```
The `clientDetails` will retrieve the following properties, because the specified prefix `client` overrides the prefix on the bean class.

```
clientDetails.host -> client.host -> myHost
clientDetails.port -> client.port -> 9081
clientDetails.endpoint -> client.endpoint -> shelf
clientDetails.getLocation() -> client.old.location -> Dublin
```
The `empty` prefix overrides the prefix set on the bean class `server`. Therefore, this will retrieve the following properties.

```
details.host -> host -> anotherHost
details.port -> port -> 9082
details.endpoint -> endpoint -> book
details.getLocation() -> old.location -> Berlin
```
In the above programmatic lookup, the configuration properties class must contain a zero-arg constructor. Otherwise, `java.lang.IllegalArgumentException` will be thrown.

If any of the property cannot be found and there is neither default value nor property is not optional, `java.util.NoSuchElementException` will be thrown.
In order to avoid this, you can supply a default value when defining the field. Alternatively, you can use `@ConfigProperty` to provide a default value.
You can also use `Optional<T>` or OptionalInt, OptionalDouble, OptionalLong as the type. If any property values cannot be converted to the specified type, `java.lang.IllegalArgumentException` will be thrown.

When using programmatic lookup of the configuration properties, the configuration properties class does not need to be annotated with
`@ConfigProperties`, if no prefix is to be supplied. The usage of the annotation `ConfigProperties` is only for supplying the prefix.
However, when using injection of the configuration properties, the configuration properties class, which should be a CDI bean, must be annotated with `@ConfigProperties`.
Otherwise, the fields will not be automatically resolved to the corresponding configuration properties.


Loading

0 comments on commit 8127e34

Please sign in to comment.