diff --git a/common/config/pom.xml b/common/config/pom.xml index a46a651ec1e..1739d6a75a6 100644 --- a/common/config/pom.xml +++ b/common/config/pom.xml @@ -29,6 +29,10 @@ Helidon Common Config + + io.helidon.common + helidon-common + 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 - io.helidon.common.features - helidon-common-features-api + io.helidon.pico + helidon-pico-api + + + io.helidon.pico + helidon-pico-runtime true - io.helidon.common.features - helidon-common-features-processor + helidon-common-features-api true @@ -108,6 +111,49 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.common.features + helidon-common-features-processor + ${helidon.version} + + + io.helidon.builder + helidon-builder-processor + ${helidon.version} + + + io.helidon.pico + helidon-pico-processor + ${helidon.version} + + + + + + + io.helidon.common.features + helidon-common-features-processor + ${helidon.version} + + + + io.helidon.pico + helidon-pico-processor + ${helidon.version} + + + + io.helidon.builder + helidon-builder-processor + ${helidon.version} + + + org.apache.maven.plugins maven-surefire-plugin diff --git a/config/config/src/main/java/io/helidon/config/ConfigProducer.java b/config/config/src/main/java/io/helidon/config/ConfigProducer.java new file mode 100644 index 00000000000..c7173a18469 --- /dev/null +++ b/config/config/src/main/java/io/helidon/config/ConfigProducer.java @@ -0,0 +1,127 @@ +/* + * 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.config; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import io.helidon.common.config.Config; +import io.helidon.common.config.ConfigException; +import io.helidon.common.config.ConfigValue; +import io.helidon.common.config.GlobalConfig; +import io.helidon.config.spi.ConfigSource; +import io.helidon.pico.api.ExternalContracts; +import io.helidon.pico.api.PicoServices; +import io.helidon.pico.api.ServiceInfoCriteria; +import io.helidon.pico.api.ServiceProvider; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import jakarta.inject.Singleton; + +@Singleton +@ExternalContracts(Config.class) +class ConfigProducer implements Config { + private final Config config; + + @Inject + ConfigProducer() { + // this should be moved to constructor injection when we support zero length lists for injection + List> serviceProviders = PicoServices.realizedServices() + .lookupAll(ServiceInfoCriteria + .builder() + .addContractImplemented(ConfigSource.class).build(), false); + + if (GlobalConfig.configured()) { + config = GlobalConfig.config(); + } else { + config = io.helidon.config.Config.builder() + .metaConfig() + .update(it -> serviceProviders.stream() + .map(Provider::get) + .map(ConfigSource.class::cast) + .forEach(it::addSource)) + .build(); + } + } + + @Override + public Key key() { + return config.key(); + } + + @Override + public Config get(String key) throws ConfigException { + return config.get(key); + } + + @Override + public Config detach() throws ConfigException { + return config.detach(); + } + + @Override + public boolean exists() { + return config.exists(); + } + + @Override + public boolean isLeaf() { + return config.isLeaf(); + } + + @Override + public boolean isObject() { + return config.isObject(); + } + + @Override + public boolean isList() { + return config.isList(); + } + + @Override + public boolean hasValue() { + return config.hasValue(); + } + + @Override + public ConfigValue as(Class type) { + return config.as(type); + } + + @Override + public ConfigValue map(Function mapper) { + return config.map(mapper); + } + + @Override + public ConfigValue> asList(Class type) throws ConfigException { + return config.asList(type); + } + + @Override + public ConfigValue> asNodeList() throws ConfigException { + return config.asNodeList(); + } + + @Override + public ConfigValue> asMap() throws ConfigException { + return config.asMap(); + } +} diff --git a/config/config/src/main/java/io/helidon/config/HelidonConfigProvider.java b/config/config/src/main/java/io/helidon/config/HelidonConfigProvider.java new file mode 100644 index 00000000000..2ba38b75932 --- /dev/null +++ b/config/config/src/main/java/io/helidon/config/HelidonConfigProvider.java @@ -0,0 +1,42 @@ +/* + * 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.config; + +import io.helidon.common.LazyValue; +import io.helidon.common.config.Config; +import io.helidon.common.config.spi.ConfigProvider; + +/** + * Service loader provider implementation for common config. + */ +public class HelidonConfigProvider implements ConfigProvider { + private static final LazyValue DEFAULT_CONFIG = LazyValue.create(io.helidon.config.Config::create); + + /** + * This should only be used by service loader and (possibly) tests. + * + * @deprecated only for {@link java.util.ServiceLoader} + */ + @Deprecated + public HelidonConfigProvider() { + } + + @Override + public Config create() { + return DEFAULT_CONFIG.get(); + } +} diff --git a/config/config/src/main/java/io/helidon/config/spi/ConfigSource.java b/config/config/src/main/java/io/helidon/config/spi/ConfigSource.java index 61e6c502d5d..5516fa34dce 100644 --- a/config/config/src/main/java/io/helidon/config/spi/ConfigSource.java +++ b/config/config/src/main/java/io/helidon/config/spi/ConfigSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. + * Copyright (c) 2017, 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. @@ -20,6 +20,7 @@ import io.helidon.config.Config; import io.helidon.config.ConfigSources; +import io.helidon.pico.api.Contract; /** * {@link Source} of configuration. @@ -62,6 +63,7 @@ * @see io.helidon.config.AbstractConfigSource * @see ConfigSources ConfigSources - access built-in implementations. */ +@Contract public interface ConfigSource extends Supplier, Source { @Override default ConfigSource get() { diff --git a/config/config/src/main/java/module-info.java b/config/config/src/main/java/module-info.java index 33d199fc437..236845d6f49 100644 --- a/config/config/src/main/java/module-info.java +++ b/config/config/src/main/java/module-info.java @@ -34,7 +34,11 @@ requires transitive io.helidon.common; requires transitive io.helidon.common.media.type; + requires io.helidon.pico.api; + requires static io.helidon.common.features.api; + requires static io.helidon.pico.runtime; + requires static jakarta.inject; exports io.helidon.config; @@ -51,6 +55,10 @@ provides io.helidon.config.spi.ConfigParser with io.helidon.config.PropertiesConfigParser; + provides io.helidon.common.config.spi.ConfigProvider + with io.helidon.config.HelidonConfigProvider; + provides io.helidon.pico.api.ModuleComponent + with io.helidon.config.Pico$$Module; // needed when running with modules - to make private methods accessible opens io.helidon.config to weld.core.impl, io.helidon.microprofile.cdi; diff --git a/config/tests/pom.xml b/config/tests/pom.xml index 8a8dedb8d02..fabd53b2071 100644 --- a/config/tests/pom.xml +++ b/config/tests/pom.xml @@ -60,5 +60,6 @@ test-mappers-2-complex test-meta-source test-parsers-1-complex + service-registry diff --git a/config/tests/service-registry/pom.xml b/config/tests/service-registry/pom.xml new file mode 100644 index 00000000000..528301f45cd --- /dev/null +++ b/config/tests/service-registry/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + + io.helidon.config.tests + helidon-config-tests-project + 4.0.0-SNAPSHOT + + helidon-config-tests-service-registry + Helidon Config Tests Service Registry + + + + io.helidon.config + helidon-config + + + jakarta.inject + jakarta.inject-api + + + io.helidon.pico + helidon-pico-runtime + + + io.helidon.config + helidon-config-yaml + test + + + io.helidon.pico + helidon-pico-testing + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.pico + helidon-pico-processor + ${helidon.version} + + + + + + + diff --git a/config/tests/service-registry/src/test/java/io/helidon/config/tests/service/registry/ConfigProducerTest.java b/config/tests/service-registry/src/test/java/io/helidon/config/tests/service/registry/ConfigProducerTest.java new file mode 100644 index 00000000000..a958458ec1c --- /dev/null +++ b/config/tests/service-registry/src/test/java/io/helidon/config/tests/service/registry/ConfigProducerTest.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package io.helidon.config.tests.service.registry; + +import java.util.Optional; + +import io.helidon.common.config.Config; +import io.helidon.common.config.GlobalConfig; +import io.helidon.pico.api.Bootstrap; +import io.helidon.pico.api.PicoServices; +import io.helidon.pico.testing.PicoTestingSupport; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class ConfigProducerTest { + @AfterEach + void reset() { + PicoTestingSupport.resetAll(); + } + + @Test + @Order(0) // this must be first, as once we set global config, this method will always fail + void testConfig() { + PicoServices.globalBootstrap(Bootstrap.builder() + .config(GlobalConfig.config()) + .build()); + + // value should be overridden using our custom config source + Config config = PicoServices.realizedServices() + .lookup(Config.class) + .get(); + + assertThat(config.get("app.value").asString().asOptional(), is(Optional.of("source"))); + } + + @Test + @Order(1) + void testExplicitConfig() { + // value should use the config as we provided it + GlobalConfig.config(io.helidon.config.Config::create, true); + + PicoServices.globalBootstrap(Bootstrap.builder() + .config(GlobalConfig.config()) + .build()); + + Config config = PicoServices.realizedServices() + .lookup(Config.class) + .get(); + + assertThat(config.get("app.value").asString().asOptional(), is(Optional.of("file"))); + } +} diff --git a/config/tests/service-registry/src/test/java/io/helidon/config/tests/service/registry/TestConfigSource.java b/config/tests/service-registry/src/test/java/io/helidon/config/tests/service/registry/TestConfigSource.java new file mode 100644 index 00000000000..e82a4984a46 --- /dev/null +++ b/config/tests/service-registry/src/test/java/io/helidon/config/tests/service/registry/TestConfigSource.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.helidon.config.tests.service.registry; + +import java.util.Optional; + +import io.helidon.common.Weight; +import io.helidon.config.ConfigException; +import io.helidon.config.spi.ConfigContent; +import io.helidon.config.spi.ConfigNode; +import io.helidon.config.spi.ConfigSource; +import io.helidon.config.spi.NodeConfigSource; +import io.helidon.pico.api.ExternalContracts; + +import jakarta.inject.Singleton; + +@Singleton +@Weight(200) +@ExternalContracts(ConfigSource.class) +class TestConfigSource implements NodeConfigSource { + + @Override + public Optional load() throws ConfigException { + return Optional.of(ConfigContent.NodeContent.builder() + .node(ConfigNode.ObjectNode.builder() + .addValue("app.value", "source") + .build()) + .build()); + } +} diff --git a/config/tests/service-registry/src/test/resources/application.yaml b/config/tests/service-registry/src/test/resources/application.yaml new file mode 100644 index 00000000000..dafcabdd7a7 --- /dev/null +++ b/config/tests/service-registry/src/test/resources/application.yaml @@ -0,0 +1,22 @@ +# +# 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. +# + +app.value: "file" + +# needed to reset state of service registry in test +pico: + permits-dynamic: true + service-lookup-caching: true diff --git a/integrations/cdi/jedis-cdi/pom.xml b/integrations/cdi/jedis-cdi/pom.xml index 9c78be50c57..f3158c3366c 100644 --- a/integrations/cdi/jedis-cdi/pom.xml +++ b/integrations/cdi/jedis-cdi/pom.xml @@ -57,6 +57,11 @@ jakarta.enterprise.cdi-api provided + + jakarta.inject + jakarta.inject-api + provided + diff --git a/integrations/oci/sdk/tests/test-application/pom.xml b/integrations/oci/sdk/tests/test-application/pom.xml index df3f4d710ac..dfb811fb47b 100644 --- a/integrations/oci/sdk/tests/test-application/pom.xml +++ b/integrations/oci/sdk/tests/test-application/pom.xml @@ -60,6 +60,10 @@ jakarta.inject-api provided + + io.helidon.config + helidon-config + io.helidon.common.testing helidon-common-testing-junit5 diff --git a/pico/api/pom.xml b/pico/api/pom.xml index a8ebcffe021..48d76f86df8 100644 --- a/pico/api/pom.xml +++ b/pico/api/pom.xml @@ -42,11 +42,6 @@ io.helidon.common helidon-common-config - - io.helidon.config - helidon-config - test - jakarta.inject jakarta.inject-api diff --git a/pico/runtime/pom.xml b/pico/runtime/pom.xml index db4cb6415d0..86afa2e348a 100644 --- a/pico/runtime/pom.xml +++ b/pico/runtime/pom.xml @@ -50,11 +50,6 @@ helidon-config-metadata true - - io.helidon.config - helidon-config - test - io.helidon.common.testing helidon-common-testing-junit5 diff --git a/pico/runtime/src/main/java/module-info.java b/pico/runtime/src/main/java/module-info.java index 10d177277f8..adfda4b7b82 100644 --- a/pico/runtime/src/main/java/module-info.java +++ b/pico/runtime/src/main/java/module-info.java @@ -21,7 +21,7 @@ requires static jakarta.inject; requires static jakarta.annotation; requires io.helidon.builder.api; - requires io.helidon.common.types; + requires transitive io.helidon.common.types; requires io.helidon.common; requires io.helidon.common.config; requires transitive io.helidon.pico.api; diff --git a/pico/tests/api/pom.xml b/pico/tests/api/pom.xml new file mode 100644 index 00000000000..8c82c15fa6a --- /dev/null +++ b/pico/tests/api/pom.xml @@ -0,0 +1,63 @@ + + + + + + io.helidon.pico.tests + helidon-pico-tests-project + 4.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + helidon-pico-tests-api + Helidon Pico Tests API + + Test pico API to remove dependency on Helidon Config, which did not allow us to have contracts in config + + + + + io.helidon.pico + helidon-pico-api + + + io.helidon.config + helidon-config + test + + + io.helidon.common.testing + helidon-common-testing-junit5 + test + + + org.hamcrest + hamcrest-all + test + + + org.junit.jupiter + junit-jupiter-api + test + + + diff --git a/pico/api/src/test/java/io/helidon/pico/api/PicoServicesConfigTest.java b/pico/tests/api/src/test/java/io/helidon/pico/api/PicoServicesConfigTest.java similarity index 100% rename from pico/api/src/test/java/io/helidon/pico/api/PicoServicesConfigTest.java rename to pico/tests/api/src/test/java/io/helidon/pico/api/PicoServicesConfigTest.java diff --git a/pico/tests/pom.xml b/pico/tests/pom.xml index 26dd1d60bc2..d960b914e82 100644 --- a/pico/tests/pom.xml +++ b/pico/tests/pom.xml @@ -48,6 +48,8 @@ resources-pico tck-jsr330 interception + api + runtime diff --git a/pico/tests/resources-pico/pom.xml b/pico/tests/resources-pico/pom.xml index 7f7d185cf5a..7710e267348 100644 --- a/pico/tests/resources-pico/pom.xml +++ b/pico/tests/resources-pico/pom.xml @@ -37,6 +37,10 @@ + + io.helidon.config + helidon-config + io.helidon.config helidon-config-metadata diff --git a/pico/tests/resources-pico/src/main/java/module-info.java b/pico/tests/resources-pico/src/main/java/module-info.java index 714aeae2c47..a5fe93b31bb 100644 --- a/pico/tests/resources-pico/src/main/java/module-info.java +++ b/pico/tests/resources-pico/src/main/java/module-info.java @@ -26,6 +26,7 @@ requires io.helidon.pico.api; requires io.helidon.pico.runtime; requires io.helidon.pico.tests.plain; + requires io.helidon.config; exports io.helidon.pico.tests.pico; exports io.helidon.pico.tests.pico.interceptor; diff --git a/pico/tests/resources-pico/src/test/java/io/helidon/pico/tests/pico/tbox/ToolBoxTest.java b/pico/tests/resources-pico/src/test/java/io/helidon/pico/tests/pico/tbox/ToolBoxTest.java index d584eef91cc..3eaaa364db1 100644 --- a/pico/tests/resources-pico/src/test/java/io/helidon/pico/tests/pico/tbox/ToolBoxTest.java +++ b/pico/tests/resources-pico/src/test/java/io/helidon/pico/tests/pico/tbox/ToolBoxTest.java @@ -179,13 +179,14 @@ void modules() { List> allModules = services.lookupAll(ModuleComponent.class); List desc = allModules.stream().map(ServiceProvider::description).collect(Collectors.toList()); // note that order matters here + // there is now config pico module as active as well assertThat("ensure that Annotation Processors are enabled in the tools module meta-inf/services", - desc, contains("Pico$$Module:ACTIVE", "Pico$$TestModule:ACTIVE")); + desc, contains("Pico$$Module:ACTIVE", "Pico$$Module:ACTIVE", "Pico$$TestModule:ACTIVE")); List names = allModules.stream() .sorted() .map(m -> m.get().named().orElse(m.get().getClass().getSimpleName() + ":null")).collect(Collectors.toList()); assertThat(names, - contains("io.helidon.pico.tests.pico", "io.helidon.pico.tests.pico/test")); + contains("io.helidon.config", "io.helidon.pico.tests.pico", "io.helidon.pico.tests.pico/test")); } /** @@ -302,7 +303,8 @@ void startupAndShutdownCallsPostConstructAndPreDestroy() { assertThat(report, hasEntry(TypeName.create("io.helidon.pico.tests.pico.stacking.OuterInterceptedImpl"), "ACTIVE->DESTROYED")); assertThat(report, hasEntry(TypeName.create("io.helidon.pico.tests.pico.stacking.InterceptedImpl"), "ACTIVE->DESTROYED")); assertThat(report, hasEntry(TypeName.create("io.helidon.pico.tests.pico.TestingSingleton"), "ACTIVE->DESTROYED")); - assertThat(report + " : expected 8 services to be present", report.size(), equalTo(8)); + // ConfigProducer is the 9th + assertThat(report + " : expected 9 services to be present", report.size(), equalTo(9)); assertThat(TestingSingleton.postConstructCount(), equalTo(1)); assertThat(TestingSingleton.preDestroyCount(), equalTo(1)); @@ -322,7 +324,8 @@ void startupAndShutdownCallsPostConstructAndPreDestroy() { .collect(Collectors.toMap(Map.Entry::getKey, e2 -> e2.getValue().startingActivationPhase().toString() + "->" + e2.getValue().finishingActivationPhase())); - assertThat(report.toString(), report.size(), is(5)); + // now contains config as well + assertThat(report.toString(), report.size(), is(6)); tearDown(); map = picoServices.shutdown().orElseThrow(); diff --git a/pico/tests/resources-pico/src/test/resources/expected/module-info.java._pico_ b/pico/tests/resources-pico/src/test/resources/expected/module-info.java._pico_ index d448102edcc..a0d9a2437aa 100644 --- a/pico/tests/resources-pico/src/test/resources/expected/module-info.java._pico_ +++ b/pico/tests/resources-pico/src/test/resources/expected/module-info.java._pico_ @@ -26,6 +26,7 @@ module io.helidon.pico.tests.pico { requires io.helidon.pico.api; requires io.helidon.pico.runtime; requires io.helidon.pico.tests.plain; + requires io.helidon.config; exports io.helidon.pico.tests.pico; exports io.helidon.pico.tests.pico.interceptor; @@ -43,4 +44,4 @@ module io.helidon.pico.tests.pico { exports io.helidon.pico.tests.pico.provider; // Pico application - @io.helidon.common.Generated(value = "io.helidon.pico.tools.ApplicationCreatorDefault", trigger = "io.helidon.pico.tools.ApplicationCreatorDefault") provides io.helidon.pico.api.Application with io.helidon.pico.tests.pico.Pico$$Application; -} +} \ No newline at end of file diff --git a/pico/tests/runtime/pom.xml b/pico/tests/runtime/pom.xml new file mode 100644 index 00000000000..e2f1adeb0bc --- /dev/null +++ b/pico/tests/runtime/pom.xml @@ -0,0 +1,63 @@ + + + + + + io.helidon.pico.tests + helidon-pico-tests-project + 4.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + helidon-pico-tests-runtime + Helidon Pico Tests Runtime + + Test pico Runtime to remove dependency on Helidon Config, which did not allow us to have services in config + + + + + io.helidon.pico + helidon-pico-runtime + + + io.helidon.config + helidon-config + test + + + io.helidon.common.testing + helidon-common-testing-junit5 + test + + + org.hamcrest + hamcrest-all + test + + + org.junit.jupiter + junit-jupiter-api + test + + + diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/DefaultInjectionPlansTest.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/DefaultInjectionPlansTest.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/DefaultInjectionPlansTest.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/DefaultInjectionPlansTest.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/DefaultPicoServicesTest.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/DefaultPicoServicesTest.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/DefaultPicoServicesTest.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/DefaultPicoServicesTest.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/HelloPicoWorldSanityTest.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/HelloPicoWorldSanityTest.java similarity index 86% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/HelloPicoWorldSanityTest.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/HelloPicoWorldSanityTest.java index 250d267cda6..0162ffb8d5e 100644 --- a/pico/runtime/src/test/java/io/helidon/pico/runtime/HelloPicoWorldSanityTest.java +++ b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/HelloPicoWorldSanityTest.java @@ -48,6 +48,7 @@ import io.helidon.pico.spi.InjectionPlan; import jakarta.inject.Singleton; +import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -66,7 +67,8 @@ * Sanity type tests only. The "real" testing is in the tests submodules. */ class HelloPicoWorldSanityTest { - private static final int EXPECTED_MODULES = 2; + // helidon-config is now one of the modules + private static final int EXPECTED_MODULES = 3; @BeforeEach void setUp() { @@ -94,8 +96,9 @@ void sanity() { assertThat(moduleProviders.size(), equalTo(EXPECTED_MODULES)); List descriptions = ServiceUtils.toDescriptions(moduleProviders); + // helidon-config is now first assertThat(descriptions, - containsInAnyOrder("EmptyModule:ACTIVE", "HelloPico$$Module:ACTIVE")); + containsInAnyOrder("Pico$$Module:ACTIVE", "EmptyModule:ACTIVE", "HelloPico$$Module:ACTIVE")); List> applications = services.lookupAll(Application.class); assertThat(applications.size(), @@ -162,8 +165,8 @@ void standardActivation() { // now activate HelloPicoWorld hello1 = helloProvider1.get(); - assertThat(hello1.sayHello(), - equalTo("Hello pico")); + MatcherAssert.assertThat(hello1.sayHello(), + equalTo("Hello pico")); assertThat(helloProvider1.currentActivationPhase(), equalTo(Phase.ACTIVE)); assertThat(helloProvider1.description(), @@ -174,10 +177,10 @@ void standardActivation() { equalTo("PicoWorldImpl:ACTIVE")); // check the post construct counts - assertThat(((HelloPicoWorldImpl) helloProvider1.get()).postConstructCallCount(), - equalTo(1)); - assertThat(((HelloPicoWorldImpl) helloProvider1.get()).preDestroyCallCount(), - equalTo(0)); + MatcherAssert.assertThat(((HelloPicoWorldImpl) helloProvider1.get()).postConstructCallCount(), + equalTo(1)); + MatcherAssert.assertThat(((HelloPicoWorldImpl) helloProvider1.get()).preDestroyCallCount(), + equalTo(0)); // deactivate just the Hello service ActivationResult result = helloProvider1.deActivator().orElseThrow() @@ -191,10 +194,10 @@ void standardActivation() { is(Phase.ACTIVE)); assertThat(helloProvider1.description(), equalTo("HelloPicoWorldImpl:DESTROYED")); - assertThat(((HelloPicoWorldImpl) hello1).postConstructCallCount(), - equalTo(1)); - assertThat(((HelloPicoWorldImpl) hello1).preDestroyCallCount(), - equalTo(1)); + MatcherAssert.assertThat(((HelloPicoWorldImpl) hello1).postConstructCallCount(), + equalTo(1)); + MatcherAssert.assertThat(((HelloPicoWorldImpl) hello1).preDestroyCallCount(), + equalTo(1)); assertThat(worldProvider1.description(), equalTo("PicoWorldImpl:ACTIVE")); } @@ -212,13 +215,13 @@ void viaInjector() { assertThat(result.success(), is(true)); HelloPicoWorld hello1 = subversiveWay.serviceRef().orElseThrow(); - assertThat(hello1.sayHello(), - equalTo("Hello pico")); - assertThat(subversiveWay.currentActivationPhase(), - equalTo(Phase.ACTIVE)); + MatcherAssert.assertThat(hello1.sayHello(), + equalTo("Hello pico")); + MatcherAssert.assertThat(subversiveWay.currentActivationPhase(), + equalTo(Phase.ACTIVE)); - assertThat(hello1, - sameInstance(subversiveWay.get())); + MatcherAssert.assertThat(hello1, + sameInstance(subversiveWay.get())); assertThat(subversiveWay, sameInstance(result.serviceProvider())); // the above is subversive because it is disconnected from the "real" activator @@ -242,9 +245,9 @@ void injectionPlanResolved() { ActivationResult result = activator.activate(ActivationRequest.builder().targetPhase(Phase.INJECTING).build()); assertThat(result.success(), is(true)); - assertThat(activator.currentActivationPhase(), is(Phase.INJECTING)); - assertThat(activator.serviceRef().orElseThrow().postConstructCallCount(), equalTo(0)); - assertThat(activator.serviceRef().orElseThrow().preDestroyCallCount(), equalTo(0)); + MatcherAssert.assertThat(activator.currentActivationPhase(), is(Phase.INJECTING)); + MatcherAssert.assertThat(activator.serviceRef().orElseThrow().postConstructCallCount(), equalTo(0)); + MatcherAssert.assertThat(activator.serviceRef().orElseThrow().preDestroyCallCount(), equalTo(0)); Map injectionPlan = result.injectionPlans(); assertThat(injectionPlan.keySet(), containsInAnyOrder( @@ -275,9 +278,9 @@ void injectionPlanResolved() { .startingPhase(activator.currentActivationPhase()) .build()); assertThat(result.success(), is(true)); - assertThat(activator.currentActivationPhase(), is(Phase.ACTIVE)); - assertThat(activator.serviceRef().orElseThrow().postConstructCallCount(), equalTo(1)); - assertThat(activator.serviceRef().orElseThrow().preDestroyCallCount(), equalTo(0)); + MatcherAssert.assertThat(activator.currentActivationPhase(), is(Phase.ACTIVE)); + MatcherAssert.assertThat(activator.serviceRef().orElseThrow().postConstructCallCount(), equalTo(1)); + MatcherAssert.assertThat(activator.serviceRef().orElseThrow().preDestroyCallCount(), equalTo(0)); // these should have happened prior, so not there any longer assertThat(result.injectionPlans(), equalTo(Map.of())); diff --git a/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/SimplePicoTestingSupport.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/SimplePicoTestingSupport.java new file mode 100644 index 00000000000..55958a5fe89 --- /dev/null +++ b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/SimplePicoTestingSupport.java @@ -0,0 +1,41 @@ +/* + * 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. + */ + +package io.helidon.pico.runtime; + +import io.helidon.pico.api.PicoServicesHolder; + +/** + * Supporting helper utilities unit-testing Pico services. + */ +class SimplePicoTestingSupport { + + /** + * Resets all internal Pico configuration instances, JVM global singletons, service registries, etc. + */ + static void resetAll() { + Holder.reset(); + } + + + @SuppressWarnings("deprecation") + private static class Holder extends PicoServicesHolder { + public static void reset() { + PicoServicesHolder.reset(); + } + } + +} diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/EmptyModule.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/EmptyModule.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/EmptyModule.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/EmptyModule.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Application.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Application.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Application.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Application.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Module.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Module.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Module.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPico$$Module.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoImpl$$picoActivator.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoImpl$$picoActivator.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoImpl$$picoActivator.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoImpl$$picoActivator.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorld.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorld.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorld.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorld.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorldImpl.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorldImpl.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorldImpl.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/HelloPicoWorldImpl.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorld.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorld.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorld.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorld.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl$$picoActivator.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl$$picoActivator.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl$$picoActivator.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl$$picoActivator.java diff --git a/pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl.java b/pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl.java similarity index 100% rename from pico/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl.java rename to pico/tests/runtime/src/test/java/io/helidon/pico/runtime/testsubjects/PicoWorldImpl.java diff --git a/pico/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.Application b/pico/tests/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.Application similarity index 100% rename from pico/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.Application rename to pico/tests/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.Application diff --git a/pico/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.ModuleComponent b/pico/tests/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.ModuleComponent similarity index 100% rename from pico/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.ModuleComponent rename to pico/tests/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.ModuleComponent index 109c35372de..e684a847a31 100644 --- a/pico/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.ModuleComponent +++ b/pico/tests/runtime/src/test/resources/META-INF/services/io.helidon.pico.api.ModuleComponent @@ -14,5 +14,5 @@ # limitations under the License. # -io.helidon.pico.runtime.testsubjects.EmptyModule io.helidon.pico.runtime.testsubjects.HelloPico$$Module +io.helidon.pico.runtime.testsubjects.EmptyModule diff --git a/pico/tools/pom.xml b/pico/tools/pom.xml index ff9e96ea3f4..8d80d3af11a 100644 --- a/pico/tools/pom.xml +++ b/pico/tools/pom.xml @@ -87,11 +87,6 @@ jakarta.annotation-api provided - - io.helidon.config - helidon-config - test - org.hamcrest hamcrest-all diff --git a/pico/tools/src/main/java/io/helidon/pico/tools/ModuleInfoDescriptorSupport.java b/pico/tools/src/main/java/io/helidon/pico/tools/ModuleInfoDescriptorSupport.java index c33edaf2507..b8d9f0c4978 100644 --- a/pico/tools/src/main/java/io/helidon/pico/tools/ModuleInfoDescriptorSupport.java +++ b/pico/tools/src/main/java/io/helidon/pico/tools/ModuleInfoDescriptorSupport.java @@ -157,6 +157,8 @@ static ModuleInfoDescriptor create(String moduleInfo, } } descriptor.addItem(provides.build()); + } else if (line.startsWith("opens ")) { + // follow up issue } else if (line.equals("}")) { break; } else {