Skip to content

Commit 1b8881f

Browse files
authored
4.x: CDI bridge for service registry + config factory improvements (helidon-io#9845)
* Add support for injection of io.helidon.config.Config * Reduce weight of the config factory, so it can be easily overridden * Fix error in javadoc of GlobalConfig * Update test to validate the new feature * Use service registry to create Config even in MP * updated service registry extension to fix a few discovered issues
1 parent 62a899c commit 1b8881f

12 files changed

Lines changed: 429 additions & 117 deletions

File tree

common/config/src/main/java/io/helidon/common/config/GlobalConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -103,7 +103,7 @@ public static Config config(Supplier<Config> config) {
103103
* @param config configuration to use
104104
* @param overwrite whether to overwrite an existing configured value
105105
* @return current global config
106-
* @deprecated use {@link io.helidon.service.registry.Services#get(Class)} instead
106+
* @deprecated use {@link io.helidon.service.registry.Services#set(Class, Object[])} instead
107107
*/
108108
@Deprecated(forRemoval = true, since = "4.2.0")
109109
public static Config config(Supplier<Config> config, boolean overwrite) {

config/config/src/main/java/io/helidon/config/ConfigProvider.java

Lines changed: 241 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,21 +16,31 @@
1616

1717
package io.helidon.config;
1818

19+
import java.time.Instant;
1920
import java.util.ArrayList;
2021
import java.util.List;
22+
import java.util.Map;
2123
import java.util.Optional;
2224
import java.util.Set;
25+
import java.util.function.Consumer;
26+
import java.util.function.Function;
27+
import java.util.function.Predicate;
2328
import java.util.function.Supplier;
2429
import java.util.stream.Collectors;
30+
import java.util.stream.Stream;
2531

26-
import io.helidon.common.config.Config;
32+
import io.helidon.common.GenericType;
33+
import io.helidon.common.Weight;
34+
import io.helidon.common.Weighted;
2735
import io.helidon.config.spi.ConfigFilter;
36+
import io.helidon.config.spi.ConfigMapper;
2837
import io.helidon.config.spi.ConfigMapperProvider;
2938
import io.helidon.config.spi.ConfigParser;
3039
import io.helidon.config.spi.ConfigSource;
3140
import io.helidon.service.registry.Service;
3241

3342
@Service.Singleton
43+
@Weight(Weighted.DEFAULT_WEIGHT - 10) // less than default, so it can be easily overridden
3444
class ConfigProvider implements Supplier<Config> {
3545
private final Config config;
3646

@@ -42,11 +52,11 @@ class ConfigProvider implements Supplier<Config> {
4252
Supplier<List<ConfigFilter>> configFilters,
4353
Supplier<List<ConfigMapperProvider>> configMappers) {
4454
if (io.helidon.common.config.GlobalConfig.configured()) {
45-
config = io.helidon.common.config.GlobalConfig.config();
55+
config = wrapCommon(io.helidon.common.config.GlobalConfig.config());
4656
} else {
47-
config = io.helidon.config.Config.builder()
57+
config = Config.builder()
4858
.update(it -> metaConfig.get().ifPresent(metaConfigInstance ->
49-
it.config(metaConfigInstance.metaConfiguration())))
59+
it.config(metaConfigInstance.metaConfiguration())))
5060
.update(it -> configSources.get()
5161
.forEach(it::addSource))
5262
.update(it -> defaultConfigSources(it, configParsers))
@@ -71,6 +81,13 @@ public Config get() {
7181
return config;
7282
}
7383

84+
private Config wrapCommon(io.helidon.common.config.Config config) {
85+
if (config instanceof Config cfg) {
86+
return cfg;
87+
}
88+
return new CommonConfigWrapper(Config.empty(), config);
89+
}
90+
7491
private void defaultConfigSources(io.helidon.config.Config.Builder configBuilder,
7592
Supplier<List<ConfigParser>> configParsers) {
7693

@@ -87,6 +104,225 @@ private void defaultConfigSources(io.helidon.config.Config.Builder configBuilder
87104
// default config source(s)
88105
MetaConfigFinder.configSources(new ArrayList<>(supportedSuffixes))
89106
.forEach(configBuilder::addSource);
107+
}
108+
109+
private static class CommonConfigWrapper implements Config {
110+
private final Config emptyConfig;
111+
private final Instant timestamp;
112+
private final io.helidon.common.config.Config delegate;
113+
114+
private CommonConfigWrapper(Config realConfig, io.helidon.common.config.Config delegate) {
115+
this(realConfig, Instant.now(), delegate);
116+
}
117+
118+
private CommonConfigWrapper(Config realConfig,
119+
Instant timestamp,
120+
io.helidon.common.config.Config delegate) {
121+
this.emptyConfig = realConfig;
122+
this.delegate = delegate;
123+
this.timestamp = timestamp;
124+
}
125+
126+
@Override
127+
public Instant timestamp() {
128+
return timestamp;
129+
}
130+
131+
@Override
132+
public Key key() {
133+
return new CommonKeyWrapper(delegate.key());
134+
}
135+
136+
@Override
137+
public String name() {
138+
return delegate.name();
139+
}
140+
141+
@Override
142+
public Config get(String key) {
143+
return new CommonConfigWrapper(emptyConfig, timestamp, delegate.get(key));
144+
}
145+
146+
@Override
147+
public Config root() {
148+
return new CommonConfigWrapper(emptyConfig, timestamp, delegate.root());
149+
}
150+
151+
@Override
152+
public Config get(Key key) {
153+
return new CommonConfigWrapper(emptyConfig, timestamp, delegate.get(key));
154+
}
155+
156+
@Override
157+
public Config detach() {
158+
return new CommonConfigWrapper(emptyConfig, timestamp, delegate.detach());
159+
}
160+
161+
@Override
162+
public Type type() {
163+
if (delegate.isList()) {
164+
return Type.LIST;
165+
}
166+
if (delegate.isObject()) {
167+
return Type.OBJECT;
168+
}
169+
if (delegate.exists()) {
170+
return Type.VALUE;
171+
}
172+
return Type.MISSING;
173+
}
174+
175+
@Override
176+
public boolean exists() {
177+
return delegate.exists();
178+
}
179+
180+
@Override
181+
public boolean isLeaf() {
182+
return delegate.isLeaf();
183+
}
184+
185+
@Override
186+
public boolean isObject() {
187+
return delegate.isObject();
188+
}
189+
190+
@Override
191+
public boolean isList() {
192+
return delegate.isList();
193+
}
194+
195+
@Override
196+
public boolean hasValue() {
197+
return delegate.hasValue();
198+
}
199+
200+
@Override
201+
public void ifExists(Consumer<Config> action) {
202+
if (delegate.exists()) {
203+
action.accept(this);
204+
}
205+
}
206+
207+
@Override
208+
public Stream<Config> traverse() {
209+
return delegate.asList(io.helidon.common.config.Config.class)
210+
.stream()
211+
.flatMap(List::stream)
212+
.map(it -> new CommonConfigWrapper(emptyConfig, timestamp, it));
213+
}
214+
215+
@Override
216+
public Stream<Config> traverse(Predicate<Config> predicate) {
217+
return traverse()
218+
.filter(predicate);
219+
}
90220

221+
@Override
222+
public <T> T convert(Class<T> type, String value) throws ConfigMappingException {
223+
return emptyConfig.convert(type, value);
224+
}
225+
226+
@Override
227+
public ConfigMapper mapper() {
228+
return emptyConfig.mapper();
229+
}
230+
231+
@SuppressWarnings("unchecked")
232+
@Override
233+
public <T> ConfigValue<T> as(GenericType<T> genericType) {
234+
if (genericType.isClass()) {
235+
return (ConfigValue<T>) as(genericType.rawType());
236+
}
237+
return ConfigValues.create(this, genericType, mapper());
238+
}
239+
240+
@Override
241+
public <T> ConfigValue<T> as(Class<T> type) {
242+
return ConfigValues.create(this, type, mapper());
243+
}
244+
245+
@Override
246+
public <T> ConfigValue<T> as(Function<Config, T> mapper) {
247+
return ConfigValues.create(this, mapper);
248+
}
249+
250+
@Override
251+
public <T> ConfigValue<List<T>> asList(Class<T> type) throws ConfigMappingException {
252+
return ConfigValues.createList(this, cfg -> cfg.as(type), cfg -> cfg.asList(type));
253+
}
254+
255+
@Override
256+
public <T> ConfigValue<List<T>> asList(Function<Config, T> mapper) throws ConfigMappingException {
257+
return ConfigValues.createList(this, cfg -> cfg.as(mapper), cfg -> cfg.asList(mapper));
258+
}
259+
260+
@Override
261+
public ConfigValue<List<Config>> asNodeList() throws ConfigMappingException {
262+
return asList(Config.class);
263+
}
264+
265+
@Override
266+
public ConfigValue<Map<String, String>> asMap() throws MissingValueException {
267+
return ConfigValues.createMap(this, mapper());
268+
}
269+
270+
@Override
271+
public io.helidon.common.config.Config get(io.helidon.common.config.Config.Key key) {
272+
return delegate.get(key);
273+
}
274+
275+
@Override
276+
public boolean equals(Object obj) {
277+
return delegate.equals(obj);
278+
}
279+
280+
@Override
281+
public int hashCode() {
282+
return delegate.hashCode();
283+
}
284+
}
285+
286+
private static class CommonKeyWrapper implements Config.Key {
287+
private final io.helidon.common.config.Config.Key delegate;
288+
289+
private CommonKeyWrapper(io.helidon.common.config.Config.Key key) {
290+
this.delegate = key;
291+
}
292+
293+
@Override
294+
public Config.Key parent() {
295+
return new CommonKeyWrapper(delegate.parent());
296+
}
297+
298+
@Override
299+
public Config.Key child(io.helidon.common.config.Config.Key key) {
300+
return new CommonKeyWrapper(delegate.child(key));
301+
}
302+
303+
@Override
304+
public boolean isRoot() {
305+
return delegate.isRoot();
306+
}
307+
308+
@Override
309+
public String name() {
310+
return delegate.name();
311+
}
312+
313+
@Override
314+
public boolean equals(Object obj) {
315+
return delegate.equals(obj);
316+
}
317+
318+
@Override
319+
public int hashCode() {
320+
return delegate.hashCode();
321+
}
322+
323+
@Override
324+
public int compareTo(io.helidon.common.config.Config.Key o) {
325+
return delegate.compareTo(o);
326+
}
91327
}
92328
}

config/config/src/main/java/io/helidon/config/ConfigValues.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import java.util.stream.Collectors;
2525

2626
import io.helidon.common.GenericType;
27+
import io.helidon.config.spi.ConfigMapper;
2728

2829
/**
2930
* Factory for config values.
@@ -146,7 +147,7 @@ static <T> ConfigValue<T> create(Config config,
146147

147148
static <T> ConfigValue<T> create(Config config,
148149
Class<T> type,
149-
ConfigMapperManager mapperManager) {
150+
ConfigMapper mapperManager) {
150151

151152
return new GenericConfigValueImpl<>(config,
152153
() -> Optional.ofNullable(mapperManager.map(config, type)),
@@ -155,7 +156,7 @@ static <T> ConfigValue<T> create(Config config,
155156

156157
static <T> ConfigValue<T> create(Config config,
157158
GenericType<T> genericType,
158-
ConfigMapperManager mapperManager) {
159+
ConfigMapper mapperManager) {
159160
return new GenericConfigValueImpl<>(config,
160161
() -> Optional.ofNullable(mapperManager.map(config, genericType)),
161162
aConfig -> aConfig.as(genericType));
@@ -190,7 +191,7 @@ static <T> ConfigValue<List<T>> createList(Config config,
190191
}
191192

192193
static ConfigValue<Map<String, String>> createMap(Config config,
193-
ConfigMapperManager mapperManager) {
194+
ConfigMapper mapperManager) {
194195

195196
Supplier<Optional<Map<String, String>>> valueSupplier = () -> {
196197
Map<?, ?> map = mapperManager.map(config, Map.class);

0 commit comments

Comments
 (0)