Skip to content

Commit 3e272ef

Browse files
committed
Fix documentations inaccuracies and missing features
1 parent 756d4b8 commit 3e272ef

6 files changed

Lines changed: 336 additions & 43 deletions

File tree

documentation/src/main/docs/config-sources/factories.md

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,46 @@ And registration in:
7878
org.acme.config.FileSystemConfigSourceFactory
7979
```
8080

81-
The `FileSystemConfigSourceFactory` look ups the configuration value for `org.acme.config.file.locations`, and uses it
81+
The `FileSystemConfigSourceFactory` look ups the configuration value for `org.acme.config.file.locations`, and uses it
8282
to set up an additional `ConfigSource`.
8383

84-
Alternatively, a `ConfigurableConfigSourceFactory` accepts a `ConfigMapping` interface to configure the `ConfigSource`:
84+
## `ConfigurableConfigSourceFactory`
85+
86+
A `ConfigurableConfigSourceFactory` accepts a `@ConfigMapping` interface to configure the `ConfigSource`, removing the
87+
need to look up configuration values manually via `ConfigSourceContext`:
8588

8689
```java
8790
@ConfigMapping(prefix = "org.acme.config.file")
8891
interface FileSystemConfig {
89-
List<URL> locations();
92+
List<URL> locations();
9093
}
9194
```
9295

9396
```java
9497
public class FileSystemConfigurableConfigSourceFactory implements ConfigurableConfigSourceFactory<FileSystemConfig> {
9598
@Override
9699
public Iterable<ConfigSource> getConfigSources(ConfigSourceContext context, FileSystemConfig config) {
97-
100+
List<ConfigSource> sources = new ArrayList<>();
101+
for (URL url : config.locations()) {
102+
try {
103+
sources.add(new PropertiesConfigSource(url, 250));
104+
} catch (IOException e) {
105+
throw new UncheckedIOException(e);
106+
}
107+
}
108+
return sources;
98109
}
99110
}
100111
```
101112

102-
With a `ConfigurableConfigSourceFactory` it is not required to look up the configuration values with
103-
`ConfigSourceContext`. The values are automatically mapped with the defined `@ConfigMapping`.
113+
And registration in:
114+
115+
```properties title="META-INF/services/io.smallrye.config.ConfigSourceFactory"
116+
org.acme.config.FileSystemConfigurableConfigSourceFactory
117+
```
118+
119+
The `ConfigMapping` interface is automatically populated from all `ConfigSource`s already initialized at the time the
120+
factory runs. Values are resolved using the same two-step initialization as a regular `ConfigSourceFactory`: sources
121+
from `ConfigSource` / `ConfigSourceProvider` registrations are available, but sources from other
122+
`ConfigSourceFactory` implementations are not.
123+

documentation/src/main/docs/config/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
| `smallrye.config.profile`<br>The main [Profile](profiles.md) to activate. | String[] | |
66
| `smallrye.config.profile.parent`<br>The parent [Profile](profiles.md#parent-profile) to activate. | String | |
77
| `smallrye.config.locations`<br>[Additional config locations](../config-sources/locations.md) to be loaded with the Config. The configuration supports multiple locations separated by a comma and each must represent a valid `java.net.URI`. | URI[] | |
8-
| `smallrye.config.mapping.validate-unknown`<br>[Validates](mappings.md#retrieval) that a `@ConfigMapping` maps every available configuration name contained in the mapping prefix. | boolean | false |
8+
| `smallrye.config.mapping.validate-unknown`<br>[Validates](mappings.md#retrieval) that a `@ConfigMapping` maps every available configuration name contained in the mapping prefix. | boolean | true |
99
| `smallrye.config.secret-handlers`<br>The names of the secret handlers to be loaded. A value of `all` loads all available secret handlers and a value of `none` skips the load. | String[] | all |
1010
| `smallrye.config.log.values`<br>Enable logging of configuration values lookup in DEBUG log level. | boolean | false |

documentation/src/main/docs/config/mappings.md

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,49 @@ Server server = config.getConfigMapping(Server.class);
9393
Config Mapping instances are cached. They are populated when the `SmallRyeConfig` instance is initialized and
9494
their values are not updated on `ConfigSource` changes.
9595

96-
For a Config Mapping to be valid, it needs to match every configuration property name contained in the `Config` under
97-
the specified prefix set in `@ConfigMapping`. This prevents unknown configuration properties in the `Config`. This
98-
behaviour can be disabled with the configuration `smallrye.config.mapping.validate-unknown=false`, or by ignoring
99-
specified paths with `io.smallrye.config.SmallRyeConfigBuilder.withMappingIgnore`.
96+
For a Config Mapping to be valid, it needs to match every configuration property name contained in the `Config` under
97+
the specified prefix set in `@ConfigMapping`. This prevents unknown configuration properties in the `Config`.
98+
99+
### Disabling validation
100+
101+
Validation can be disabled entirely in two ways:
102+
103+
Via configuration property:
104+
105+
```properties
106+
smallrye.config.mapping.validate-unknown=false
107+
```
108+
109+
Or programmatically via the builder:
110+
111+
```java
112+
SmallRyeConfig config = new SmallRyeConfigBuilder()
113+
.withMapping(Server.class)
114+
.withValidateUnknown(false)
115+
.build();
116+
```
117+
118+
### Ignoring specific paths
119+
120+
Rather than disabling validation entirely, specific paths can be ignored with
121+
`SmallRyeConfigBuilder#withMappingIgnore`. The path argument supports three patterns:
122+
123+
| Pattern | Meaning |
124+
|-----------|--------------------------------------------------------|
125+
| `foo.bar` | Ignores the exact configuration name `foo.bar` |
126+
| `foo.*` | Ignores all direct child names under `foo` |
127+
| `foo.**` | Ignores all names under `foo` at any level (recursive) |
128+
129+
```java
130+
SmallRyeConfig config = new SmallRyeConfigBuilder()
131+
.withMapping(Server.class)
132+
.withMappingIgnore("server.foo") // ignore the exact name
133+
.withMappingIgnore("server.extras.*") // ignore direct children of server.extras
134+
.withMappingIgnore("server.legacy.**") // ignore everything under server.legacy
135+
.build();
136+
```
137+
138+
Multiple calls to `withMappingIgnore` accumulate — each call adds to the set of ignored paths.
100139

101140
## Defaults
102141

@@ -281,6 +320,35 @@ The `@ConfigMapping` annotation support the following naming stategies:
281320
- VERBATIM - The method name is used as is to map the configuration property.
282321
- SNAKE_CASE - The method name is derived by replacing case changes with an underscore to map the configuration property.
283322

323+
### `beanStyleGetters`
324+
325+
The `beanStyleGetters` attribute (default `false`) enables matching bean-style getter names (`get`/`is` prefixed) to
326+
their property name equivalent. For example, `getHost()` and `isEnabled()` map to the properties `host` and `enabled`
327+
respectively:
328+
329+
```java
330+
@ConfigMapping(prefix = "server", beanStyleGetters = true)
331+
public interface Server {
332+
String getHost();
333+
334+
int getPort();
335+
336+
boolean isEnabled();
337+
}
338+
```
339+
340+
```properties
341+
server.host=localhost
342+
server.port=8080
343+
server.enabled=true
344+
```
345+
346+
!!! warning
347+
348+
Bean-style getter matching allows multiple method names to match the same configuration name. For instance,
349+
`getFoo` and `isFoo` both match `foo`, which may not be intended. Prefer simple method names that match
350+
one-to-one with their configuration names.
351+
284352
## Conversion
285353

286354
A config mapping interface support automatic conversions of all types available for conversion in `Config`.
@@ -527,6 +595,26 @@ Map<String, Alias> localhost = server.aliases.get("localhost");
527595

528596
If the unnamed key (in this case `localhost`) is explicitly set in a property name, the mapping will throw an error.
529597

598+
The `eager` attribute (default `true`) controls whether the unnamed key entry is included in the `Map` when its values
599+
come only from defaults. When `eager = false`, the unnamed key entry is excluded from the `Map` unless at least one
600+
of its values is explicitly set in a configuration source:
601+
602+
```java
603+
@ConfigMapping(prefix = "server")
604+
public interface Server {
605+
@WithUnnamedKey(value = "localhost", eager = false)
606+
Map<String, Alias> aliases();
607+
608+
interface Alias {
609+
@WithDefault("localhost")
610+
String name();
611+
}
612+
}
613+
```
614+
615+
With `eager = false` and no properties set, `server.aliases` returns an empty `Map`. With `eager = true` (the
616+
default), it returns a `Map` with the key `localhost` populated from the `@WithDefault`.
617+
530618
### `@WithKeys`
531619

532620
The `io.smallrye.config.WithKeys` annotation allows to define which `Map` keys must be loaded by
@@ -596,9 +684,9 @@ and the size is `1`.
596684

597685
```java
598686
Server server = config.getConfigMapping(Server.class);
599-
Map<String, Alias> localhost = server.aliases.get("localhost");
600-
Map<String, Alias> any = server.aliases.get("any");
601-
Map<String, Alias> any = server.aliases.get("prod");
687+
Map<String, Alias> localhost = server.aliases().get("localhost");
688+
Map<String, Alias> any = server.aliases().get("any");
689+
Map<String, Alias> prod = server.aliases().get("prod");
602690
```
603691

604692
## Optionals

documentation/src/main/docs/config/secret-keys.md

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,109 @@
55
In SmallRye Config, a secret configuration may be expressed as `${handler::value}`, where the `handler` is the name of
66
a `io.smallrye.config.SecretKeysHandler` to decode or decrypt the `value` separated by a double colon `::`.
77

8-
It is possible to create a custom `SecretKeysHandler` and provide different ways to decode or decrypt configuration
9-
values.
8+
It is possible to create a custom `SecretKeysHandler` and provide different ways to decode or decrypt configuration
9+
values.
1010

11-
A custom `SecretKeysHandler` requires an implementation of `io.smallrye.config.SecretKeysHandler` or
12-
`io.smallrye.config.SecretKeysHandlerFactory`. Each implementation requires registration via the `ServiceLoader`
11+
A custom `SecretKeysHandler` requires an implementation of `io.smallrye.config.SecretKeysHandler` or
12+
`io.smallrye.config.SecretKeysHandlerFactory`. Each implementation requires registration via the `ServiceLoader`
1313
mechanism, either in `META-INF/services/io.smallrye.config.SecretKeysHandler` or
1414
`META-INF/services/io.smallrye.config.SecretKeysHandlerFactory` files.
1515

16+
### Custom `SecretKeysHandler`
17+
18+
A direct `SecretKeysHandler` implementation is suitable when the handler needs no configuration of its own:
19+
20+
```java
21+
public class Base64SecretKeysHandler implements SecretKeysHandler {
22+
@Override
23+
public String decode(final String secret) {
24+
return new String(Base64.getDecoder().decode(secret));
25+
}
26+
27+
@Override
28+
public String getName() {
29+
return "base64";
30+
}
31+
}
32+
```
33+
34+
```title="META-INF/services/io.smallrye.config.SecretKeysHandler"
35+
org.acme.config.Base64SecretKeysHandler
36+
```
37+
38+
A secret value encoded with the `base64` handler can then be expressed as:
39+
40+
```properties
41+
my.secret=${base64::SGVsbG8gV29ybGQ=}
42+
```
43+
44+
### `SecretKeysHandlerFactory`
45+
46+
When a handler requires configuration from other config sources (for example, a key or a credential read from
47+
the config), use `SecretKeysHandlerFactory` instead. The factory receives a `ConfigSourceContext` that provides
48+
access to all config sources initialized before the factory runs:
49+
50+
```java
51+
public class VaultSecretKeysHandlerFactory implements SecretKeysHandlerFactory {
52+
@Override
53+
public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context) {
54+
ConfigValue token = context.getValue("vault.token");
55+
return new VaultSecretKeysHandler(token.getValue());
56+
}
57+
58+
@Override
59+
public String getName() {
60+
return "vault";
61+
}
62+
}
63+
```
64+
65+
```properties title="META-INF/services/io.smallrye.config.SecretKeysHandlerFactory"
66+
org.acme.config.VaultSecretKeysHandlerFactory
67+
```
68+
69+
### `LazySecretKeysHandler`
70+
71+
`SecretKeysHandlerFactory` initializes during the first phase of `SmallRyeConfig` bootstrap, alongside regular
72+
`ConfigSource` and `ConfigSourceProvider` registrations. This means that config values produced by a
73+
`ConfigSourceFactory` are **not** yet available when the factory's `getSecretKeysHandler` is called.
74+
75+
For handlers that depend on sources provided by a `ConfigSourceFactory`, wrap an inner `SecretKeysHandlerFactory`
76+
in a `SecretKeysHandlerFactory.LazySecretKeysHandler`. The inner factory's `getSecretKeysHandler` is only invoked
77+
the first time a value actually needs to be decoded, by which point all sources — including those from
78+
`ConfigSourceFactory` — are fully initialized:
79+
80+
```java
81+
public class VaultSecretKeysHandlerFactory implements SecretKeysHandlerFactory {
82+
@Override
83+
public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context) {
84+
return new LazySecretKeysHandler(new SecretKeysHandlerFactory() {
85+
@Override
86+
public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context) {
87+
// This runs lazily, after all sources are ready.
88+
ConfigValue token = context.getValue("vault.token");
89+
return new VaultSecretKeysHandler(token.getValue());
90+
}
91+
92+
@Override
93+
public String getName() {
94+
return "vault";
95+
}
96+
});
97+
}
98+
99+
@Override
100+
public String getName() {
101+
return "vault";
102+
}
103+
}
104+
```
105+
106+
!!! warning
107+
108+
The inner factory wrapped by `LazySecretKeysHandler` is what defers initialization. Do not call
109+
`context.getValue` in the outer `getSecretKeysHandler`; only the inner factory's `getSecretKeysHandler` (invoked lazily) may resolve configuration values.
110+
16111
!!!danger
17112

18113
It is not possible to mix Secret Keys Expressions with Property Expressions.

documentation/src/main/docs/converters/custom.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class CustomValueConverter implements Converter<CustomValue> {
4343
And registration in:
4444

4545
```properties title="META-INF/services/org.eclipse.microprofile.config.spi.Converter"
46-
org.acme.config.CustomValue
46+
org.acme.config.CustomValueConverter
4747
```
4848

4949
!!! warning

0 commit comments

Comments
 (0)