org.junit.jupiter
junit-jupiter-api
diff --git a/common/config/src/main/java/io/helidon/common/config/GlobalConfig.java b/common/config/src/main/java/io/helidon/common/config/GlobalConfig.java
new file mode 100644
index 00000000000..fd661893a5c
--- /dev/null
+++ b/common/config/src/main/java/io/helidon/common/config/GlobalConfig.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.common.config;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import io.helidon.common.HelidonServiceLoader;
+import io.helidon.common.LazyValue;
+import io.helidon.common.config.spi.ConfigProvider;
+
+/**
+ * Global configuration can be set by a user before any Helidon code is invoked, to override default discovery
+ * of configuration done by Helidon components.
+ *
+ * If method {@link #config(java.util.function.Supplier)} is called before Helidon is started, Helidon will only use that
+ * configuration.
+ *
+ * You may still use custom instances of configuration when using configurable APIs directly.
+ */
+public final class GlobalConfig {
+ private static final Config EMPTY = Config.empty();
+ private static final LazyValue DEFAULT_CONFIG = LazyValue.create(() -> {
+ List providers = HelidonServiceLoader.create(ServiceLoader.load(ConfigProvider.class))
+ .asList();
+ // no implementations available, use empty configuration
+ if (providers.isEmpty()) {
+ return EMPTY;
+ }
+ // there is a valid provider, let's use its default configuration
+ return providers.get(0)
+ .create();
+ });
+ private static final AtomicReference CONFIG = new AtomicReference<>();
+
+ private GlobalConfig() {
+ }
+
+ /**
+ * Whether a global configuration has already been configured.
+ *
+ * @return {@code true} if there is a global configuration set already, {@code false} otherwise
+ */
+ public static boolean configured() {
+ return CONFIG.get() != null;
+ }
+
+ /**
+ * Global configuration instance.
+ *
+ * @return Helidon shared configuration instance if configured, or an empty configuration if not
+ * @see #config(java.util.function.Supplier)
+ * @see #config(java.util.function.Supplier, boolean)
+ */
+ public static Config config() {
+ return configured() ? CONFIG.get() : DEFAULT_CONFIG.get();
+ }
+
+ /**
+ * Set global configuration if not yet configured.
+ *
+ * @param config configuration supplier to use if config is not yet configured
+ * @return used global configuration instance
+ */
+ public static Config config(Supplier config) {
+ return config(config, false);
+ }
+
+ /**
+ * Set global configuration.
+ *
+ * @param config configuration to use
+ * @param overwrite whether to overwrite an existing configured value
+ * @return current global config
+ */
+ public static Config config(Supplier config, boolean overwrite) {
+ Objects.requireNonNull(config);
+
+ if (overwrite || !configured()) {
+ // there is a certain risk we may do this twice, if two components try to set global config in parallel.
+ // as the result was already unclear (as order matters), we do not need to be 100% thread safe here
+ CONFIG.set(config.get());
+ }
+ return CONFIG.get();
+ }
+}
diff --git a/common/config/src/main/java/io/helidon/common/config/spi/ConfigProvider.java b/common/config/src/main/java/io/helidon/common/config/spi/ConfigProvider.java
new file mode 100644
index 00000000000..37e84552c07
--- /dev/null
+++ b/common/config/src/main/java/io/helidon/common/config/spi/ConfigProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.common.config.spi;
+
+import io.helidon.common.config.Config;
+
+/**
+ * Service loader provider interface to discover config implementation that would be used to
+ * obtain a default configuration instance.
+ */
+public interface ConfigProvider {
+ /**
+ * Create the default configuration instance.
+ *
+ * @return a new configuration
+ */
+ Config create();
+}
diff --git a/common/config/src/main/java/io/helidon/common/config/spi/package-info.java b/common/config/src/main/java/io/helidon/common/config/spi/package-info.java
new file mode 100644
index 00000000000..c527c80edc3
--- /dev/null
+++ b/common/config/src/main/java/io/helidon/common/config/spi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates.
+ *
+ * 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.
+ */
+
+/**
+ * SPI to load an implementation to be able to discover the default configuration of current environment.
+ */
+package io.helidon.common.config.spi;
diff --git a/common/config/src/main/java/module-info.java b/common/config/src/main/java/module-info.java
index 789c2d599f3..3ada0559dd8 100644
--- a/common/config/src/main/java/module-info.java
+++ b/common/config/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,5 +18,10 @@
* Helidon Common Config Library.
*/
module io.helidon.common.config {
+ requires io.helidon.common;
+
exports io.helidon.common.config;
+ exports io.helidon.common.config.spi;
+
+ uses io.helidon.common.config.spi.ConfigProvider;
}
diff --git a/config/config/pom.xml b/config/config/pom.xml
index 0f2202db22d..41ddf7a4087 100644
--- a/config/config/pom.xml
+++ b/config/config/pom.xml
@@ -59,14 +59,17 @@
helidon-logging-jul