+ */
+public abstract class OciMetricsSupportFactory {
+
+ private Config ociMetricsConfig;
+
+ /**
+ * Creates a new instance of the factory.
+ */
+ protected OciMetricsSupportFactory() {
+ }
+
+ /**
+ * Registers OCI metrics using the configuration and the provided monitoring client by preparing
+ * an {@link io.helidon.integrations.oci.metrics.OciMetricsSupport} instance and then calling back to the
+ * subclass to activate that instance with, for example, routing.
+ *
+ * @param rootConfig root config node
+ * @param monitoringClient {@link Monitoring} instance to use in preparing the {@code OciMetricsSupport} instance
+ */
+ protected void registerOciMetrics(Config rootConfig, Monitoring monitoringClient) {
+ ociMetricsConfig = rootConfig.get(configKey());
+ OciMetricsSupport.Builder builder = ociMetricsSupportBuilder(rootConfig, ociMetricsConfig, monitoringClient);
+ if (builder.enabled()) {
+ activateOciMetricsSupport(rootConfig, ociMetricsConfig, builder);
+ }
+ }
+
+ /**
+ * Returns the builder for constructing a new {@link io.helidon.integrations.oci.metrics.OciMetricsSupport} instance,
+ * initialized using the config retrieved using the {@link #configKey()} return value and the provided
+ * {@link com.oracle.bmc.monitoring.Monitoring} instance.
+ *
+ * @param rootConfig root {@link io.helidon.config.Config} node
+ * @param ociMetricsConfig config node for the OCI metrics settings
+ * @param monitoring monitoring implementation to be used in preparing the {@code OciMetricsSupport.Builder}.
+ * @return resulting builder
+ */
+ protected OciMetricsSupport.Builder ociMetricsSupportBuilder(Config rootConfig,
+ Config ociMetricsConfig,
+ Monitoring monitoring) {
+ this.ociMetricsConfig = ociMetricsConfig;
+ return OciMetricsSupport.builder()
+ .config(ociMetricsConfig)
+ .monitoringClient(monitoring);
+ }
+
+ /**
+ * Returns the OCI metrics config node used to set up the {@code OciMetricsSupport} instance.
+ *
+ * @return config node controlling the OCI metrics support behavior
+ */
+ protected Config ociMetricsConfig() {
+ return ociMetricsConfig;
+ }
+
+ /**
+ * Returns the config key to use for retrieving OCI metrics settings from the root config.
+ *
+ * @return config key for OCI metrics settings
+ */
+ protected abstract String configKey();
+
+ /**
+ * Activates OCI metrics support.
+ *
+ * @param rootConfig root config node
+ * @param ociMetricsConfig OCI metrics configuration
+ * @param builder {@link io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder} instance
+ */
+ protected abstract void activateOciMetricsSupport(Config rootConfig,
+ Config ociMetricsConfig,
+ OciMetricsSupport.Builder builder);
+}
diff --git a/integrations/oci/metrics/metrics/src/main/java/module-info.java b/integrations/oci/metrics/metrics/src/main/java/module-info.java
index 3442c806eb6..81c1a943eaf 100644
--- a/integrations/oci/metrics/metrics/src/main/java/module-info.java
+++ b/integrations/oci/metrics/metrics/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 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.
@@ -21,7 +21,6 @@
module io.helidon.integrations.oci.metrics {
requires io.helidon.http;
- requires io.helidon.metrics.api;
requires java.logging;
requires oci.java.sdk.common;
@@ -29,6 +28,7 @@
requires transitive io.helidon.common;
requires transitive io.helidon.config;
+ requires transitive io.helidon.metrics.api;
requires transitive io.helidon.webserver;
requires transitive oci.java.sdk.monitoring;
From e3d8a35b2e2ae55598c4a9f37e26e89e6583f20b Mon Sep 17 00:00:00 2001
From: Jorge Bescos Gascon
Date: Thu, 16 May 2024 18:18:20 +0200
Subject: [PATCH 007/245] 4.x: helidon-common sources JAR contains absolute
paths #8761 (#8762)
* 4.x: helidon-common sources JAR contains absolute paths #8761
Signed-off-by: Jorge Bescos Gascon
---
common/common/pom.xml | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/common/common/pom.xml b/common/common/pom.xml
index 19137590ee6..c415ee7680a 100644
--- a/common/common/pom.xml
+++ b/common/common/pom.xml
@@ -42,18 +42,28 @@
-
-
- src/main/resources
-
-
- src/main/templates
- true
- ${project.build.directory}/generated-sources/templates
-
-
-
+
+ maven-resources-plugin
+
+
+ copy-resources
+ generate-sources
+
+ resources
+
+
+ ${project.build.directory}/generated-sources/templates
+
+
+ src/main/templates
+ true
+
+
+
+
+
+ org.codehaus.mojobuild-helper-maven-plugin
@@ -72,6 +82,6 @@
-
+
From 4f8043d2f899bab333dc721bb3dcd866a78b68c9 Mon Sep 17 00:00:00 2001
From: Jorge Bescos Gascon
Date: Fri, 17 May 2024 22:15:12 +0200
Subject: [PATCH 008/245] 4.x: Ability to Inject MockBeans in Helidon #7694
(#8674)
* 4.x: Ability to Inject MockBeans in Helidon #7694
Signed-off-by: Jorge Bescos Gascon
---
all/pom.xml | 4 +
bom/pom.xml | 5 +
dependencies/pom.xml | 6 ++
microprofile/testing/junit5/pom.xml | 1 -
.../junit5/src/main/java/module-info.java | 2 +-
microprofile/testing/mocking/pom.xml | 49 ++++++++++
.../testing/mocking/MockBean.java | 40 ++++++++
.../mocking/MockBeansCdiExtension.java | 92 ++++++++++++++++++
.../testing/mocking/package-info.java | 20 ++++
.../mocking/src/main/java/module-info.java | 31 ++++++
microprofile/testing/pom.xml | 1 +
microprofile/testing/testng/pom.xml | 1 -
.../testng/src/main/java/module-info.java | 2 +-
microprofile/tests/testing/junit5/pom.xml | 10 ++
.../testing/junit5/TestMockBeanAnswer.java | 75 ++++++++++++++
.../testing/junit5/TestMockBeanField.java | 97 +++++++++++++++++++
.../testing/junit5/TestMockBeanParameter.java | 85 ++++++++++++++++
microprofile/tests/testing/testng/pom.xml | 10 ++
.../testing/testng/TestMockBeanField.java | 82 ++++++++++++++++
19 files changed, 609 insertions(+), 4 deletions(-)
create mode 100644 microprofile/testing/mocking/pom.xml
create mode 100644 microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBean.java
create mode 100644 microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBeansCdiExtension.java
create mode 100644 microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/package-info.java
create mode 100644 microprofile/testing/mocking/src/main/java/module-info.java
create mode 100644 microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanAnswer.java
create mode 100644 microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanField.java
create mode 100644 microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanParameter.java
create mode 100644 microprofile/tests/testing/testng/src/test/java/io/helidon/microprofile/tests/testing/testng/TestMockBeanField.java
diff --git a/all/pom.xml b/all/pom.xml
index f97cd9e2c7c..10792daf584 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -773,6 +773,10 @@
io.helidon.messaging.mockhelidon-messaging-mock
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-mocking
+ io.helidon.logginghelidon-logging-common
diff --git a/bom/pom.xml b/bom/pom.xml
index 52fd63482cc..b5897ef2833 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -1028,6 +1028,11 @@
helidon-messaging-mock${helidon.version}
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-mocking
+ ${helidon.version}
+ io.helidon.logging
diff --git a/dependencies/pom.xml b/dependencies/pom.xml
index 03904a97473..a401bbbcf00 100644
--- a/dependencies/pom.xml
+++ b/dependencies/pom.xml
@@ -40,6 +40,7 @@
1.181.3.51.0.0
+ 1.14.141.16.01.29.1.6
@@ -501,6 +502,11 @@
+
+ net.bytebuddy
+ byte-buddy
+ ${version.lib.bytebuddy}
+ jakarta.websocketjakarta.websocket-api
diff --git a/microprofile/testing/junit5/pom.xml b/microprofile/testing/junit5/pom.xml
index 28b08ded417..ba84605ac03 100644
--- a/microprofile/testing/junit5/pom.xml
+++ b/microprofile/testing/junit5/pom.xml
@@ -67,7 +67,6 @@
hamcrest-coretest
-
diff --git a/microprofile/testing/junit5/src/main/java/module-info.java b/microprofile/testing/junit5/src/main/java/module-info.java
index 9799b1b0e0b..f9514942be0 100644
--- a/microprofile/testing/junit5/src/main/java/module-info.java
+++ b/microprofile/testing/junit5/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
diff --git a/microprofile/testing/mocking/pom.xml b/microprofile/testing/mocking/pom.xml
new file mode 100644
index 00000000000..f87d42df88e
--- /dev/null
+++ b/microprofile/testing/mocking/pom.xml
@@ -0,0 +1,49 @@
+
+
+
+
+ 4.0.0
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-project
+ 4.0.0-SNAPSHOT
+ ../pom.xml
+
+
+ helidon-microprofile-testing-mocking
+ Helidon Microprofile Testing Mocking
+
+
+ Integration with Mocking to support tests with CDI injection
+
+
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+ provided
+
+
+ org.mockito
+ mockito-core
+ provided
+
+
+
+
diff --git a/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBean.java b/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBean.java
new file mode 100644
index 00000000000..b52fe2de66c
--- /dev/null
+++ b/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBean.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024 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.microprofile.testing.mocking;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.mockito.Answers;
+
+/**
+ * A field annotated with @MockBean will be mocked by Mockito
+ * and injected in every place it is referenced.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface MockBean {
+
+ /**
+ * The {@link Answers} type to use on the mock.
+ * Defaults to {@link Answers#RETURNS_DEFAULTS}
+ * @return the answer type
+ */
+ Answers answer() default Answers.RETURNS_DEFAULTS;
+}
diff --git a/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBeansCdiExtension.java b/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBeansCdiExtension.java
new file mode 100644
index 00000000000..eb68cc5c011
--- /dev/null
+++ b/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/MockBeansCdiExtension.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2024 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.microprofile.testing.mocking;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.inject.literal.InjectLiteral;
+import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
+import jakarta.enterprise.inject.spi.AnnotatedParameter;
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.enterprise.inject.spi.Extension;
+import jakarta.enterprise.inject.spi.ProcessAnnotatedType;
+import jakarta.enterprise.inject.spi.WithAnnotations;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
+
+/**
+ * CDI extension for Mocking implementation.
+ */
+public class MockBeansCdiExtension implements Extension {
+
+ private final Map, MockBean> mocks = new HashMap<>();
+
+ void processMockBean(@Observes @WithAnnotations(MockBean.class) ProcessAnnotatedType> obj) throws Exception {
+ var configurator = obj.configureAnnotatedType();
+ configurator.fields().forEach(field -> {
+ MockBean mockBean = field.getAnnotated().getAnnotation(MockBean.class);
+ if (mockBean != null) {
+ Field f = field.getAnnotated().getJavaMember();
+ // Adds @Inject to be more user friendly
+ field.add(InjectLiteral.INSTANCE);
+ Class> fieldType = f.getType();
+ mocks.put(fieldType, mockBean);
+ }
+ });
+ configurator.constructors().forEach(constructor -> {
+ processMockBeanParameters(constructor.getAnnotated().getParameters());
+ });
+ }
+
+ private void processMockBeanParameters(List extends AnnotatedParameter>> parameters) {
+ parameters.stream().forEach(parameter -> {
+ MockBean mockBean = parameter.getAnnotation(MockBean.class);
+ if (mockBean != null) {
+ Class> parameterType = parameter.getJavaParameter().getType();
+ mocks.put(parameterType, mockBean);
+ }
+ });
+ }
+
+ void registerOtherBeans(@Observes AfterBeanDiscovery event, BeanManager beanManager) {
+ // Register all mocks
+ mocks.entrySet().forEach(entry -> {
+ event.addBean()
+ .addType(entry.getKey())
+ .scope(ApplicationScoped.class)
+ .alternative(true)
+ .createWith(inst -> {
+ Set> beans = beanManager.getBeans(MockSettings.class);
+ if (!beans.isEmpty()) {
+ Bean> bean = beans.iterator().next();
+ MockSettings mockSettings = (MockSettings) beanManager.getReference(bean, MockSettings.class,
+ beanManager.createCreationalContext(null));
+ return Mockito.mock(entry.getKey(), mockSettings);
+ } else {
+ return Mockito.mock(entry.getKey(), Mockito.withSettings().defaultAnswer(entry.getValue().answer()));
+ }
+ })
+ .priority(0);
+ });
+ }
+}
diff --git a/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/package-info.java b/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/package-info.java
new file mode 100644
index 00000000000..5c8a0a150ac
--- /dev/null
+++ b/microprofile/testing/mocking/src/main/java/io/helidon/microprofile/testing/mocking/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+/**
+ * Helidon implementation of mocking.
+ */
+package io.helidon.microprofile.testing.mocking;
diff --git a/microprofile/testing/mocking/src/main/java/module-info.java b/microprofile/testing/mocking/src/main/java/module-info.java
new file mode 100644
index 00000000000..7c0fcc72974
--- /dev/null
+++ b/microprofile/testing/mocking/src/main/java/module-info.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+/**
+ * Mock Beans extension module to run CDI tests.
+ */
+module io.helidon.microprofile.testing.mocking {
+
+ requires jakarta.inject;
+ requires org.mockito;
+
+ requires transitive jakarta.cdi;
+
+ exports io.helidon.microprofile.testing.mocking;
+
+ provides jakarta.enterprise.inject.spi.Extension
+ with io.helidon.microprofile.testing.mocking.MockBeansCdiExtension;
+}
diff --git a/microprofile/testing/pom.xml b/microprofile/testing/pom.xml
index bc159631fc6..3bd61ad034c 100644
--- a/microprofile/testing/pom.xml
+++ b/microprofile/testing/pom.xml
@@ -35,5 +35,6 @@
junit5testng
+ mocking
diff --git a/microprofile/testing/testng/pom.xml b/microprofile/testing/testng/pom.xml
index 1fe04815c11..6f2e6a0b19a 100644
--- a/microprofile/testing/testng/pom.xml
+++ b/microprofile/testing/testng/pom.xml
@@ -57,7 +57,6 @@
io.helidon.confighelidon-config-yaml-mp
-
org.testngtestng
diff --git a/microprofile/testing/testng/src/main/java/module-info.java b/microprofile/testing/testng/src/main/java/module-info.java
index c2e60fd39aa..f5a4aa1feb3 100644
--- a/microprofile/testing/testng/src/main/java/module-info.java
+++ b/microprofile/testing/testng/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 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.
diff --git a/microprofile/tests/testing/junit5/pom.xml b/microprofile/tests/testing/junit5/pom.xml
index 70c211a3e4b..88ff79a886b 100644
--- a/microprofile/tests/testing/junit5/pom.xml
+++ b/microprofile/tests/testing/junit5/pom.xml
@@ -55,5 +55,15 @@
hamcrest-coretest
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-mocking
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
diff --git a/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanAnswer.java b/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanAnswer.java
new file mode 100644
index 00000000000..1a5874f323e
--- /dev/null
+++ b/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanAnswer.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2024 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.microprofile.tests.testing.junit5;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+
+import io.helidon.microprofile.testing.junit5.AddBean;
+import io.helidon.microprofile.testing.junit5.HelidonTest;
+import io.helidon.microprofile.testing.mocking.MockBean;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Answers;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
+
+@HelidonTest
+@AddBean(TestMockBeanAnswer.Resource.class)
+@AddBean(TestMockBeanAnswer.Service.class)
+class TestMockBeanAnswer {
+
+ @MockBean(answer = Answers.CALLS_REAL_METHODS)
+ private Service service;
+ @Inject
+ private WebTarget target;
+
+ @Test
+ void injectionTest() {
+ String response = target.path("/test").request().get(String.class);
+ assertThat(response, is("Not Mocked"));
+ Mockito.when(service.test()).thenReturn("Mocked");
+ response = target.path("/test").request().get(String.class);
+ assertThat(response, is("Mocked"));
+ }
+
+ @Path("/test")
+ public static class Resource {
+
+ @Inject
+ private Service service;
+
+ @GET
+ public String test() {
+ return service.test();
+ }
+ }
+
+ static class Service {
+
+ String test() {
+ return "Not Mocked";
+ }
+
+ }
+}
diff --git a/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanField.java b/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanField.java
new file mode 100644
index 00000000000..695c783a0c0
--- /dev/null
+++ b/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanField.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2024 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.microprofile.tests.testing.junit5;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+
+import io.helidon.microprofile.testing.junit5.AddBean;
+import io.helidon.microprofile.testing.junit5.HelidonTest;
+import io.helidon.microprofile.testing.mocking.MockBean;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Answers;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
+
+@HelidonTest
+@AddBean(TestMockBeanField.Resource.class)
+@AddBean(TestMockBeanField.Service.class)
+@AddBean(TestMockBeanField.OtherService.class)
+class TestMockBeanField {
+
+ // Without @Inject
+ @MockBean
+ private Service service;
+ // With @Inject
+ @Inject
+ @MockBean
+ private OtherService otherService;
+ @Inject
+ private WebTarget target;
+
+ @Test
+ void injectionTest() {
+ String response = target.path("/test").request().get(String.class);
+ // Defaults to specified in @Produces
+ assertThat(response, is("Not Mocked"));
+ Mockito.when(service.test()).thenReturn("Mocked");
+ response = target.path("/test").request().get(String.class);
+ assertThat(response, is("Mocked"));
+ Mockito.when(otherService.test()).thenReturn("Mocked");
+ assertThat(otherService.test(), is("Mocked"));
+ }
+
+ @Produces
+ MockSettings mockSettings() {
+ return Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS);
+ }
+
+ @Path("/test")
+ public static class Resource {
+
+ @Inject
+ private Service service;
+
+ @GET
+ public String test() {
+ return service.test();
+ }
+ }
+
+ static class Service {
+
+ String test() {
+ return "Not Mocked";
+ }
+
+ }
+
+ static class OtherService {
+
+ String test() {
+ return "OtherService";
+ }
+
+ }
+}
diff --git a/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanParameter.java b/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanParameter.java
new file mode 100644
index 00000000000..f511095882c
--- /dev/null
+++ b/microprofile/tests/testing/junit5/src/test/java/io/helidon/microprofile/tests/testing/junit5/TestMockBeanParameter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2024 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.microprofile.tests.testing.junit5;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+
+import io.helidon.microprofile.testing.junit5.AddBean;
+import io.helidon.microprofile.testing.junit5.HelidonTest;
+import io.helidon.microprofile.testing.mocking.MockBean;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Answers;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
+
+@HelidonTest
+@AddBean(TestMockBeanParameter.Resource.class)
+@AddBean(TestMockBeanParameter.Service.class)
+class TestMockBeanParameter {
+
+ private final Service service;
+ private final WebTarget target;
+
+ @Inject
+ TestMockBeanParameter(@MockBean Service service, WebTarget target) {
+ this.service = service;
+ this.target = target;
+ }
+
+ @Test
+ void injectionTest() {
+ String response = target.path("/test").request().get(String.class);
+ // Defaults to specified in @Produces
+ assertThat(response, is(""));
+ Mockito.when(service.test()).thenReturn("Mocked");
+ response = target.path("/test").request().get(String.class);
+ assertThat(response, is("Mocked"));
+ }
+
+ @Produces
+ MockSettings mockSettings() {
+ return Mockito.withSettings().defaultAnswer(Answers.RETURNS_DEFAULTS);
+ }
+
+ @Path("/test")
+ public static class Resource {
+
+ @Inject
+ private Service service;
+
+ @GET
+ public String test() {
+ return service.test();
+ }
+ }
+
+ static class Service {
+
+ String test() {
+ return "Not Mocked";
+ }
+
+ }
+}
diff --git a/microprofile/tests/testing/testng/pom.xml b/microprofile/tests/testing/testng/pom.xml
index ed741b47056..57e0332df7f 100644
--- a/microprofile/tests/testing/testng/pom.xml
+++ b/microprofile/tests/testing/testng/pom.xml
@@ -54,5 +54,15 @@
hamcrest-coretest
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-mocking
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
diff --git a/microprofile/tests/testing/testng/src/test/java/io/helidon/microprofile/tests/testing/testng/TestMockBeanField.java b/microprofile/tests/testing/testng/src/test/java/io/helidon/microprofile/tests/testing/testng/TestMockBeanField.java
new file mode 100644
index 00000000000..2f63646acce
--- /dev/null
+++ b/microprofile/tests/testing/testng/src/test/java/io/helidon/microprofile/tests/testing/testng/TestMockBeanField.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2024 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.microprofile.tests.testing.testng;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+
+import io.helidon.microprofile.testing.mocking.MockBean;
+import io.helidon.microprofile.testing.testng.AddBean;
+import io.helidon.microprofile.testing.testng.HelidonTest;
+
+import org.mockito.Answers;
+import org.mockito.MockSettings;
+import org.mockito.Mockito;
+import org.testng.annotations.Test;
+
+@HelidonTest
+@AddBean(TestMockBeanField.Resource.class)
+@AddBean(TestMockBeanField.Service.class)
+class TestMockBeanField {
+
+ @Inject
+ @MockBean
+ private Service service;
+ @Inject
+ private WebTarget target;
+
+ @Test
+ void injectionTest() {
+ String response = target.path("/test").request().get(String.class);
+ // Defaults to specified in @Produces
+ assertThat(response, is("Not Mocked"));
+ Mockito.when(service.test()).thenReturn("Mocked");
+ response = target.path("/test").request().get(String.class);
+ assertThat(response, is("Mocked"));
+ }
+
+ @Produces
+ MockSettings mockSettings() {
+ return Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS);
+ }
+
+ @Path("/test")
+ public static class Resource {
+
+ @Inject
+ private Service service;
+
+ @GET
+ public String test() {
+ return service.test();
+ }
+ }
+
+ static class Service {
+
+ String test() {
+ return "Not Mocked";
+ }
+
+ }
+}
From 9544c363d053e6c992c807ca53080b6078dffcb1 Mon Sep 17 00:00:00 2001
From: Tim Quinn
Date: Fri, 17 May 2024 17:40:54 -0500
Subject: [PATCH 009/245] Fix problems with tracing data propagation (#8742)
* Fix problems with tracing data propagation
Signed-off-by: Tim Quinn
* Add Javadoc comment
---------
Signed-off-by: Tim Quinn
---
.../jaeger/JaegerDataPropagationProvider.java | 70 +++----------
.../OpenTelemetryDataPropagationProvider.java | 99 +++++++++++++++++++
.../src/main/java/module-info.java | 3 +
.../opentelemetry/TestDataPropagation.java | 97 ++++++++++++++++++
.../observe/tracing/TracingObserver.java | 4 +-
5 files changed, 214 insertions(+), 59 deletions(-)
create mode 100644 tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java
create mode 100644 tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestDataPropagation.java
diff --git a/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerDataPropagationProvider.java b/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerDataPropagationProvider.java
index 5e634273a7a..ba8b1c6f35b 100644
--- a/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerDataPropagationProvider.java
+++ b/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerDataPropagationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2021, 2024 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.
@@ -16,75 +16,29 @@
package io.helidon.tracing.providers.jaeger;
-import io.helidon.common.context.Contexts;
-import io.helidon.common.context.spi.DataPropagationProvider;
-import io.helidon.tracing.Scope;
-import io.helidon.tracing.Span;
-import io.helidon.tracing.Tracer;
-import io.helidon.tracing.providers.opentelemetry.OpenTelemetryTracerProvider;
+import io.helidon.tracing.providers.opentelemetry.OpenTelemetryDataPropagationProvider;
/**
* A data propagation provider for Jaeger. Makes sure span are properly propagated
* across threads managed by {@link io.helidon.common.context.ContextAwareExecutorService}.
+ *
+ * Because the Jaeger client uses OpenTelemetry, our data propagation for Jaeger is identical
+ * to that for OTel.
+ *
*/
-public class JaegerDataPropagationProvider implements DataPropagationProvider {
- private static final System.Logger LOGGER = System.getLogger(JaegerDataPropagationProvider.class.getName());
+public class JaegerDataPropagationProvider extends OpenTelemetryDataPropagationProvider {
- /**
- * Jaeger Context.
- */
- public static class JaegerContext {
- private final Span span;
- private final Tracer tracer;
- private Scope scope;
-
- JaegerContext(Tracer tracer, Span span) {
- this.tracer = tracer;
- this.span = span;
- }
-
- Scope scope() {
- return scope;
- }
- }
-
- /**
- * Closes scope in primary thread and returns a context to activate
- * new scope in secondary thread.
- *
- * @return active span.
- */
@Override
public JaegerContext data() {
- return Contexts.context().map(context -> context.get(Span.class).map(span -> {
- Tracer tracer = context.get(Tracer.class).orElseGet(OpenTelemetryTracerProvider::globalTracer);
- return new JaegerContext(tracer, span);
- }).orElse(null)).orElse(null);
- }
-
- /**
- * Activates scope in secondary thread.
- *
- * @param context the context.
- */
- @Override
- public void propagateData(JaegerContext context) {
- if (context != null) {
- context.scope = Span.current().map(Span::activate).orElse(null);
- }
+ return new JaegerContext(super.data());
}
/**
- * Closes scope in secondary thread.
+ * Jaeger context.
*/
- @Override
- public void clearData(JaegerContext context) {
- if (context != null && context.scope != null) {
- try {
- context.scope.close();
- } catch (Exception e) {
- LOGGER.log(System.Logger.Level.TRACE, "Cannot close tracing span", e);
- }
+ public static class JaegerContext extends OpenTelemetryDataPropagationProvider.OpenTelemetryContext {
+ JaegerContext(OpenTelemetryContext delegate) {
+ super(delegate.tracer(), delegate.span());
}
}
}
diff --git a/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java b/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java
new file mode 100644
index 00000000000..2c7c9561b3f
--- /dev/null
+++ b/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryDataPropagationProvider.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2024 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.tracing.providers.opentelemetry;
+
+import io.helidon.common.context.Contexts;
+import io.helidon.common.context.spi.DataPropagationProvider;
+import io.helidon.tracing.Scope;
+import io.helidon.tracing.Span;
+import io.helidon.tracing.Tracer;
+
+/**
+ * A data propagation provider for OpenTelemetry which makes sure active spans are properly propagated
+ * across threads managed by {@link io.helidon.common.context.ContextAwareExecutorService}.
+ */
+public class OpenTelemetryDataPropagationProvider
+ implements DataPropagationProvider {
+
+ private static final System.Logger LOGGER = System.getLogger(OpenTelemetryDataPropagationProvider.class.getName());
+
+ @Override
+ public OpenTelemetryContext data() {
+ return Contexts.context().map(context -> context.get(Span.class).map(span -> {
+ Tracer tracer = context.get(Tracer.class).orElseGet(OpenTelemetryTracerProvider::globalTracer);
+ return new OpenTelemetryContext(tracer, span);
+ }).orElse(null)).orElse(null);
+ }
+
+ @Override
+ public void clearData(OpenTelemetryContext context) {
+ if (context != null && context.scope != null) {
+ try {
+ context.scope.close();
+ } catch (Exception e) {
+ LOGGER.log(System.Logger.Level.TRACE, "Cannot close tracing span", e);
+ }
+ }
+ }
+
+ @Override
+ public void propagateData(OpenTelemetryContext context) {
+ if (context != null) {
+ context.scope = context.span.activate();
+ }
+ }
+
+ /**
+ * OpenTelementry context.
+ */
+ public static class OpenTelemetryContext {
+ private final Span span;
+ private final Tracer tracer;
+ private Scope scope;
+
+ protected OpenTelemetryContext(Tracer tracer, Span span) {
+ this.tracer = tracer;
+ this.span = span;
+ }
+
+ /**
+ * Return the current scope.
+ *
+ * @return current scope, null if the span in this context is not active
+ */
+ public Scope scope() {
+ return scope;
+ }
+
+ /**
+ * Return the tracer.
+ *
+ * @return tracer from the context
+ */
+ public Tracer tracer() {
+ return tracer;
+ }
+
+ /**
+ * Return the span.
+ *
+ * @return span from the context
+ */
+ public Span span() {
+ return span;
+ }
+ }
+}
diff --git a/tracing/providers/opentelemetry/src/main/java/module-info.java b/tracing/providers/opentelemetry/src/main/java/module-info.java
index 735990d5394..1389848cdea 100644
--- a/tracing/providers/opentelemetry/src/main/java/module-info.java
+++ b/tracing/providers/opentelemetry/src/main/java/module-info.java
@@ -37,4 +37,7 @@
provides io.helidon.tracing.spi.TracerProvider
with io.helidon.tracing.providers.opentelemetry.OpenTelemetryTracerProvider;
+ provides io.helidon.common.context.spi.DataPropagationProvider
+ with io.helidon.tracing.providers.opentelemetry.OpenTelemetryDataPropagationProvider;
+
}
\ No newline at end of file
diff --git a/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestDataPropagation.java b/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestDataPropagation.java
new file mode 100644
index 00000000000..34f44257ffa
--- /dev/null
+++ b/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestDataPropagation.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2024 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.tracing.providers.opentelemetry;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.helidon.common.context.Context;
+import io.helidon.common.context.Contexts;
+import io.helidon.common.testing.junit5.OptionalMatcher;
+import io.helidon.tracing.Scope;
+import io.helidon.tracing.Span;
+import io.helidon.tracing.SpanContext;
+import io.helidon.tracing.Tracer;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+
+class TestDataPropagation {
+
+ private final OpenTelemetryDataPropagationProvider provider = new OpenTelemetryDataPropagationProvider();
+ private final ExecutorService executor = Contexts.wrap(Executors.newVirtualThreadPerTaskExecutor());
+
+ @Test
+ void testSyncPropagation() {
+ Context context = Context.create();
+ Tracer tracer = OpenTelemetryTracer.builder()
+ .serviceName("test-prop")
+ .build();
+ context.register(tracer);
+ Span span = tracer.spanBuilder("test-span").start();
+ context.register(span);
+
+ try (Scope scope = span.activate()) {
+ context.register(scope);
+ Contexts.runInContext(context, () -> {
+ OpenTelemetryDataPropagationProvider.OpenTelemetryContext data = provider.data();
+ assertThat("Scope before prop ", data.scope(), is(nullValue()));
+
+ provider.propagateData(data);
+ assertThat("Scope during prop has been closed", data.scope().isClosed(), is(false));
+
+ provider.clearData(data);
+ assertThat("Scope after prop has been closed", data.scope().isClosed(), is(true));
+ });
+ }
+ }
+
+ @Test
+ void testAsyncPropagation() {
+ Context context = Context.create();
+ Tracer tracer = Tracer.global();
+ context.register(tracer);
+ Span span = tracer.spanBuilder("test-async-span").start();
+ context.register(span);
+ SpanContext spanContext = span.context();
+ AtomicReference> asyncSpanRef = new AtomicReference<>();
+ try (Scope scope = span.activate()) {
+ context.register(scope);
+ Contexts.runInContext(context, () ->
+ {
+ try {
+ asyncSpanRef.set(executor.submit(() -> {
+ return Span.current();
+ }).get(5, TimeUnit.SECONDS));
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ Optional asyncSpanContext = asyncSpanRef.get().map(Span::context);
+ assertThat("Span ID",
+ asyncSpanContext.map(SpanContext::spanId),
+ OptionalMatcher.optionalValue(is(spanContext.spanId())));
+ }
+}
diff --git a/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserver.java b/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserver.java
index b70936c1585..619e82638db 100644
--- a/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserver.java
+++ b/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -216,6 +216,8 @@ Find configuration of the web server span (can customize name, disable etc.)
.start();
context.register(span.context());
+ context.register(span);
+ context.register(tracer);
context.register(TracingConfig.class, span.context());
/*
From c837dd72bd836c4e7319ad178d4681d4e3771a96 Mon Sep 17 00:00:00 2001
From: Santiago Pericas-Geertsen
Date: Mon, 20 May 2024 09:18:56 -0400
Subject: [PATCH 010/245] Switched implementation of
MpEnvironmentVariablesSource to use an LRU cache (#8768)
* Avoids caching misses, as these may grow the internal cache and result in an OOM exception. See issue 8767.
Signed-off-by: Santiago Pericas-Geertsen
* Switched implementation to use an LRU cache to limit the capacity but still cache the missed lookups. See issue 8767.
Signed-off-by: Santiago Pericas-Geertsen
---------
Signed-off-by: Santiago Pericas-Geertsen
---
config/config-mp/pom.xml | 4 ++
.../mp/MpEnvironmentVariablesSource.java | 31 ++++++++++----
.../config-mp/src/main/java/module-info.java | 3 +-
.../mp/MpEnvironmentVariablesSourceTest.java | 40 +++++++++++++++++++
4 files changed, 70 insertions(+), 8 deletions(-)
create mode 100644 config/config-mp/src/test/java/io/helidon/config/mp/MpEnvironmentVariablesSourceTest.java
diff --git a/config/config-mp/pom.xml b/config/config-mp/pom.xml
index df6652aac74..3c7daedebe8 100644
--- a/config/config-mp/pom.xml
+++ b/config/config-mp/pom.xml
@@ -50,6 +50,10 @@
org.eclipse.microprofile.configmicroprofile-config-api
+
+ io.helidon.common
+ helidon-common-configurable
+ io.helidon.confighelidon-config-metadata
diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java
index eb1b0dd1daa..dccc95f61cc 100644
--- a/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java
+++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java
@@ -17,10 +17,12 @@
package io.helidon.config.mp;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
+import io.helidon.common.configurable.LruCache;
+
import jakarta.annotation.Priority;
import org.eclipse.microprofile.config.spi.ConfigSource;
@@ -29,12 +31,27 @@ class MpEnvironmentVariablesSource implements ConfigSource {
static final int MY_DEFAULT_ORDINAL = 300;
private static final Pattern DISALLOWED_CHARS = Pattern.compile("[^a-zA-Z0-9_]");
private static final String UNDERSCORE = "_";
+ private static final int MAX_CACHE_SIZE = 10000;
private final Map env;
- private final Map cache = new ConcurrentHashMap<>();
+ private final LruCache cache;
MpEnvironmentVariablesSource() {
+ this(MAX_CACHE_SIZE);
+ }
+
+ MpEnvironmentVariablesSource(int cacheSize) {
this.env = Map.copyOf(System.getenv());
+ this.cache = LruCache.builder().capacity(cacheSize).build();
+ }
+
+ /**
+ * Access internal cache, used for testing.
+ *
+ * @return internal cache
+ */
+ LruCache cache() {
+ return cache;
}
@Override
@@ -51,24 +68,24 @@ public Map getProperties() {
public String getValue(String propertyName) {
// environment variable config source is immutable - we can safely cache all requested keys, so we
// do not execute the regular expression on every get
- return cache.computeIfAbsent(propertyName, theKey -> {
+ return cache.computeValue(propertyName, () -> {
// According to the spec, we have three ways of looking for a property
// 1. Exact match
String result = env.get(propertyName);
if (null != result) {
- return new Cached(result);
+ return Optional.of(new Cached(result));
}
// 2. replace non alphanumeric characters with _
String rule2 = rule2(propertyName);
result = env.get(rule2);
if (null != result) {
- return new Cached(result);
+ return Optional.of(new Cached(result));
}
// 3. replace same as above, but uppercase
String rule3 = rule2.toUpperCase();
result = env.get(rule3);
- return new Cached(result);
- }).value;
+ return Optional.of(new Cached(result));
+ }).map(cached -> cached.value).orElse(null);
}
@Override
diff --git a/config/config-mp/src/main/java/module-info.java b/config/config-mp/src/main/java/module-info.java
index e63242779fb..eef7afa4e4d 100644
--- a/config/config-mp/src/main/java/module-info.java
+++ b/config/config-mp/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -23,6 +23,7 @@
requires io.helidon.common;
requires io.helidon.config;
requires jakarta.annotation;
+ requires io.helidon.common.configurable;
requires static io.helidon.config.metadata;
diff --git a/config/config-mp/src/test/java/io/helidon/config/mp/MpEnvironmentVariablesSourceTest.java b/config/config-mp/src/test/java/io/helidon/config/mp/MpEnvironmentVariablesSourceTest.java
new file mode 100644
index 00000000000..2ddcdecb925
--- /dev/null
+++ b/config/config-mp/src/test/java/io/helidon/config/mp/MpEnvironmentVariablesSourceTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024 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.mp;
+
+import java.util.UUID;
+import java.util.stream.IntStream;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class MpEnvironmentVariablesSourceTest {
+
+ private static final int MAX_GROWTH = 10;
+
+ @Test
+ void testCacheMaxGrowth() {
+ MpEnvironmentVariablesSource source = new MpEnvironmentVariablesSource(MAX_GROWTH);
+ assertThat(source.cache().size(), is(0));
+ IntStream.range(0, 5 * MAX_GROWTH).forEach(i -> {
+ String random = UUID.randomUUID().toString().toLowerCase();
+ source.getValue(random); // should cache and discard oldest after MAX_GROWTH
+ });
+ assertThat(source.cache().size(), is(MAX_GROWTH));
+ }
+}
From 6f1884a14a2868c90b594c951abb194eb48ef609 Mon Sep 17 00:00:00 2001
From: Romain Grecourt
Date: Mon, 20 May 2024 16:53:31 -0700
Subject: [PATCH 011/245] 4.x - AbstractConfigurableExtension native-image fix
(#8771)
* Update AbstractConfigurableExtension to lookup config values lazily.
This resolves an issue with native-image where the config values are fixed at build-time.
---
.../configurable/etc/spotbugs/exclude.xml | 30 ++
.../cdi/common-cdi/configurable/pom.xml | 4 +
.../AbstractConfigurableExtension.java | 16 +-
.../cdi/configurable/ComputedProperties.java | 258 ++++++++++++++++++
4 files changed, 301 insertions(+), 7 deletions(-)
create mode 100644 integrations/cdi/common-cdi/configurable/etc/spotbugs/exclude.xml
create mode 100644 integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/ComputedProperties.java
diff --git a/integrations/cdi/common-cdi/configurable/etc/spotbugs/exclude.xml b/integrations/cdi/common-cdi/configurable/etc/spotbugs/exclude.xml
new file mode 100644
index 00000000000..f259e792fb5
--- /dev/null
+++ b/integrations/cdi/common-cdi/configurable/etc/spotbugs/exclude.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integrations/cdi/common-cdi/configurable/pom.xml b/integrations/cdi/common-cdi/configurable/pom.xml
index 117cc591369..dc47ea0dc2c 100644
--- a/integrations/cdi/common-cdi/configurable/pom.xml
+++ b/integrations/cdi/common-cdi/configurable/pom.xml
@@ -28,6 +28,10 @@
helidon-integrations-cdi-configurableHelidon CDI Integrations Common Configurable
+
+ etc/spotbugs/exclude.xml
+
+
diff --git a/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java
index 3c632ec0236..3488788d5c2 100644
--- a/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java
+++ b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java
@@ -256,21 +256,23 @@ protected final Properties put(String name, Properties properties) {
* @see #addBean(BeanConfigurator, Named, Properties)
*/
protected final void initializeNamedProperties() {
- this.namedProperties.clear();
- Set extends String> allConfigPropertyNames = this.configPropertyNames();
+ Map computedProperties = new HashMap<>();
+ Set allConfigPropertyNames = configPropertyNames();
if (!allConfigPropertyNames.isEmpty()) {
for (String configPropertyName : allConfigPropertyNames) {
- Optional propertyValue = this.configPropertyValue(configPropertyName);
+ Optional propertyValue = configPropertyValue(configPropertyName);
if (propertyValue.isPresent()) {
- Matcher matcher = this.matcher(configPropertyName);
+ Matcher matcher = matcher(configPropertyName);
if (matcher != null && matcher.matches()) {
- this.namedProperties.computeIfAbsent(this.name(matcher), n -> new Properties())
- .setProperty(this.propertyName(matcher), propertyValue.get());
+ computedProperties.computeIfAbsent(name(matcher), n -> new ComputedProperties(this::configPropertyValue))
+ .computedKeys().put(propertyName(matcher), configPropertyName);
}
}
}
}
- this.namedProperties.putAll(this.explicitlySetProperties);
+ namedProperties.clear();
+ namedProperties.putAll(computedProperties);
+ namedProperties.putAll(explicitlySetProperties);
}
/**
diff --git a/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/ComputedProperties.java b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/ComputedProperties.java
new file mode 100644
index 00000000000..1b98bbf8d49
--- /dev/null
+++ b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/ComputedProperties.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2024 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.integrations.cdi.configurable;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * A lazy read-only {@link Properties} implementation backed by a {@link Function}.
+ */
+final class ComputedProperties extends Properties {
+
+ private static final VarHandle ENTRIES;
+
+ static {
+ try {
+ ENTRIES = MethodHandles.lookup().findVarHandle(ComputedProperties.class, "entries", Map.class);
+ } catch (ReflectiveOperationException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private volatile Map
-
- io.helidon.metrics
- helidon-metrics-provider-tests
- io.helidon.metrics.providershelidon-metrics-providers-micrometer
@@ -1034,10 +1030,6 @@
io.helidon.injecthelidon-inject-processor
-
- io.helidon.inject
- helidon-inject-maven-plugin
- io.helidon.injecthelidon-inject-testing
diff --git a/bom/pom.xml b/bom/pom.xml
index b5897ef2833..75a5fd3aa6e 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -380,11 +380,6 @@
helidon-metrics-system-meters${helidon.version}
-
- io.helidon.metrics
- helidon-metrics-provider-tests
- ${helidon.version}
- io.helidon.metrics.providershelidon-metrics-providers-micrometer
@@ -665,12 +660,6 @@
helidon-tracing-tracer-resolver${helidon.version}
-
-
- io.helidon.tracing
- helidon-tracing-provider-tests
- ${helidon.version}
- io.helidon.tracing
@@ -1362,11 +1351,6 @@
helidon-inject-processor${helidon.version}
-
- io.helidon.inject
- helidon-inject-maven-plugin
- ${helidon.version}
- io.helidon.injecthelidon-inject-testing
diff --git a/inject/tests/resources-inject/pom.xml b/inject/tests/resources-inject/pom.xml
index b20a68748b7..fcc59a36920 100644
--- a/inject/tests/resources-inject/pom.xml
+++ b/inject/tests/resources-inject/pom.xml
@@ -49,6 +49,7 @@
io.helidon.injecthelidon-inject-maven-plugin
+ ${helidon.version}providedtrue
diff --git a/inject/tests/tck-jsr330/pom.xml b/inject/tests/tck-jsr330/pom.xml
index 3618a306061..a2e3acfd988 100644
--- a/inject/tests/tck-jsr330/pom.xml
+++ b/inject/tests/tck-jsr330/pom.xml
@@ -54,6 +54,7 @@
io.helidon.injecthelidon-inject-maven-plugin
+ ${helidon.version}providedtrue
diff --git a/metrics/providers/pom.xml b/metrics/providers/pom.xml
index 1ab8c0f23e2..2c3e01261c9 100644
--- a/metrics/providers/pom.xml
+++ b/metrics/providers/pom.xml
@@ -43,6 +43,7 @@
io.helidon.metricshelidon-metrics-provider-tests
+ ${helidon.version}test
diff --git a/tracing/providers/pom.xml b/tracing/providers/pom.xml
index 9ad7bdc2c57..65a94297128 100644
--- a/tracing/providers/pom.xml
+++ b/tracing/providers/pom.xml
@@ -44,6 +44,7 @@
io.helidon.tracinghelidon-tracing-provider-tests
+ ${helidon.version}test
From fddb60f8132a4397ce960a4b45c82eb9ced308b0 Mon Sep 17 00:00:00 2001
From: Santiago Pericas-Geertsen
Date: Wed, 22 May 2024 08:00:30 -0400
Subject: [PATCH 014/245] Forward port of issue 6829 to Helidon 4. (#8786)
Signed-off-by: Santiago Pericas-Geertsen
---
.../mp/MpEnvironmentVariablesSource.java | 3 +-
.../helidon/config/EnvironmentVariables.java | 4 +-
.../io/helidon/config/PropertiesFilter.java | 108 ++++++++++++++++++
.../helidon/config/PropertiesFilterTest.java | 84 ++++++++++++++
4 files changed, 196 insertions(+), 3 deletions(-)
create mode 100644 config/config/src/main/java/io/helidon/config/PropertiesFilter.java
create mode 100644 config/config/src/test/java/io/helidon/config/PropertiesFilterTest.java
diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java
index dccc95f61cc..d84494ca3f1 100644
--- a/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java
+++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpEnvironmentVariablesSource.java
@@ -22,6 +22,7 @@
import java.util.regex.Pattern;
import io.helidon.common.configurable.LruCache;
+import io.helidon.config.PropertiesFilter;
import jakarta.annotation.Priority;
import org.eclipse.microprofile.config.spi.ConfigSource;
@@ -41,7 +42,7 @@ class MpEnvironmentVariablesSource implements ConfigSource {
}
MpEnvironmentVariablesSource(int cacheSize) {
- this.env = Map.copyOf(System.getenv());
+ this.env = Map.copyOf(PropertiesFilter.create(System.getProperties()).filter(System.getenv()));
this.cache = LruCache.builder().capacity(cacheSize).build();
}
diff --git a/config/config/src/main/java/io/helidon/config/EnvironmentVariables.java b/config/config/src/main/java/io/helidon/config/EnvironmentVariables.java
index 360540c83cc..6ebc0870273 100644
--- a/config/config/src/main/java/io/helidon/config/EnvironmentVariables.java
+++ b/config/config/src/main/java/io/helidon/config/EnvironmentVariables.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2024 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.
@@ -101,7 +101,7 @@ public static boolean shouldAlias(final String name) {
* @return An unmodifiable copy of {@link System#getenv()} including aliases.
*/
public static Map expand() {
- return expand(System.getenv());
+ return expand(PropertiesFilter.create(System.getProperties()).filter(System.getenv()));
}
/**
diff --git a/config/config/src/main/java/io/helidon/config/PropertiesFilter.java b/config/config/src/main/java/io/helidon/config/PropertiesFilter.java
new file mode 100644
index 00000000000..8504bdb7ee6
--- /dev/null
+++ b/config/config/src/main/java/io/helidon/config/PropertiesFilter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2024 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.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Filter properties with provided and default filter pattern.
+ */
+public class PropertiesFilter {
+ /**
+ * Key filter system property.
+ */
+ static final String KEY_FILTER_PROPERTY = "io.helidon.config.env-filter.key";
+ /**
+ * Value filter system property.
+ */
+ static final String VALUE_FILTER_PROPERTY = "io.helidon.config.env-filter.value";
+ /**
+ * Use default filter system property.
+ */
+ static final String USE_DEFAULT_FILTER_PROPERTY = "io.helidon.config.env-filter.use-default";
+ /**
+ * Regex separator property.
+ */
+ static final String SEPARATOR_FILTER_PROPERTY = "io.helidon.config.env-filter.separator";
+ private static final String REGEX_BASH_FUNC = "BASH_FUNC_(.*?)%%";
+ private static final Pattern PATTERN_BASH_FUNC = Pattern.compile(REGEX_BASH_FUNC);
+ private static final List DEFAULT_KEY_PATTERNS = List.of(PATTERN_BASH_FUNC);
+ private final List keyFilters;
+ private final List valueFilters;
+
+ private PropertiesFilter(List keyFilters, List valueFilters, boolean useDefault) {
+ this.keyFilters = convert(keyFilters);
+ this.valueFilters = convert(valueFilters);
+ if (useDefault) {
+ this.keyFilters.addAll(DEFAULT_KEY_PATTERNS);
+ }
+ }
+
+ /**
+ * Create a {@link PropertiesFilter} instance.
+ *
+ * @param properties System Properties
+ * @return a {@link PropertiesFilter} instance
+ */
+ public static PropertiesFilter create(Properties properties) {
+ Objects.requireNonNull(properties, "properties are null");
+ List keyFilters = parseFilterProperty(KEY_FILTER_PROPERTY, properties);
+ List valueFilters = parseFilterProperty(VALUE_FILTER_PROPERTY, properties);
+ boolean useDefault = Boolean.parseBoolean(properties.getProperty(USE_DEFAULT_FILTER_PROPERTY, "true"));
+ return new PropertiesFilter(keyFilters, valueFilters, useDefault);
+ }
+
+ /**
+ * Filter provided properties with this filter.
+ *
+ * @param properties to be filtered
+ * @return the filtered properties
+ */
+ public Map filter(Map properties) {
+ return properties.entrySet().stream()
+ .filter(entry -> matches(keyFilters, entry.getKey()))
+ .filter(entry -> matches(valueFilters, entry.getValue()))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private static List parseFilterProperty(String property, Properties properties) {
+ String separator = properties.getProperty(SEPARATOR_FILTER_PROPERTY, ",");
+ String resolved = properties.getProperty(property);
+ if (resolved == null) {
+ return List.of();
+ }
+ return Arrays.asList(resolved.split(separator));
+ }
+
+ private List convert(List list) {
+ return list.stream()
+ .map(Pattern::compile)
+ .collect(Collectors.toCollection(LinkedList::new));
+ }
+
+ private boolean matches(List patterns, String value) {
+ return patterns.stream()
+ .noneMatch(matcher -> matcher.matcher(value).matches());
+ }
+}
diff --git a/config/config/src/test/java/io/helidon/config/PropertiesFilterTest.java b/config/config/src/test/java/io/helidon/config/PropertiesFilterTest.java
new file mode 100644
index 00000000000..6272393b134
--- /dev/null
+++ b/config/config/src/test/java/io/helidon/config/PropertiesFilterTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2024 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.Map;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static io.helidon.config.PropertiesFilter.KEY_FILTER_PROPERTY;
+import static io.helidon.config.PropertiesFilter.USE_DEFAULT_FILTER_PROPERTY;
+import static io.helidon.config.PropertiesFilter.VALUE_FILTER_PROPERTY;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.is;
+
+public class PropertiesFilterTest {
+
+ @BeforeEach
+ void clearProperties() {
+ clear();
+ }
+
+ @AfterAll
+ static void cleanUp() {
+ clear();
+ }
+ private static void clear() {
+ System.clearProperty(KEY_FILTER_PROPERTY);
+ System.clearProperty(VALUE_FILTER_PROPERTY);
+ System.clearProperty(USE_DEFAULT_FILTER_PROPERTY);
+ }
+
+ @Test
+ void testDefaultFilter() {
+ PropertiesFilter filter = PropertiesFilter.create(System.getProperties());
+ Map filtered = filter.filter(Map.of("foo", "bar",
+ "BASH_FUNC_%%", "bash function"));
+ assertThat(filtered.size(), is(1));
+ assertThat(filtered.keySet(), contains("foo"));
+ }
+
+ @Test
+ void testWithoutDefaultFilter() {
+ System.setProperty(USE_DEFAULT_FILTER_PROPERTY, "false");
+ PropertiesFilter filter = PropertiesFilter.create(System.getProperties());
+
+ Map result = filter.filter(Map.of("foo", "bar",
+ "BASH_FUNC_foo%%", "bash function"));
+ assertThat(result.size(), is(2));
+ assertThat(result.keySet(), contains("foo", "BASH_FUNC_foo%%"));
+ }
+
+ @Test
+ void testFilter() {
+ System.setProperty(KEY_FILTER_PROPERTY, "foo.(.*?),(.*?).foo.(.*?)");
+ System.setProperty(VALUE_FILTER_PROPERTY, "bar.(.*?)");
+ PropertiesFilter filter = PropertiesFilter.create(System.getProperties());
+
+ Map result = filter.filter(Map.of("foo", "bar",
+ "foo.bar", "foo.bar",
+ "bar.foo.bar", "bar.foo.bar",
+ "bar.foo", "bar.foo",
+ "BASH_FUNC_foo%%", "bash function"));
+ assertThat(result.size(), is(1));
+ assertThat(result.keySet(), contains("foo"));
+ }
+}
From 6074fa837f2d544234413165790e1443937d8f7a Mon Sep 17 00:00:00 2001
From: Joe DiPol
Date: Wed, 22 May 2024 12:00:49 -0700
Subject: [PATCH 015/245] Update setup-java for snapshot workflow (#8788)
---
.github/workflows/snapshotrelease.yaml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/snapshotrelease.yaml b/.github/workflows/snapshotrelease.yaml
index 211807efa11..b3af9e2a64e 100644
--- a/.github/workflows/snapshotrelease.yaml
+++ b/.github/workflows/snapshotrelease.yaml
@@ -9,7 +9,7 @@ on:
env:
JAVA_VERSION: '21'
- JAVA_DISTRO: 'oracle.com'
+ JAVA_DISTRO: 'oracle'
MAVEN_HTTP_ARGS: '-Dmaven.wagon.httpconnectionManager.ttlSeconds=60 -Dmaven.wagon.http.retryHandler.count=3'
concurrency:
@@ -26,10 +26,10 @@ jobs:
with:
fetch-depth: '0'
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: oracle-actions/setup-java@v1.3.2
+ uses: actions/setup-java@v4.1.0
with:
- website: ${{ env.JAVA_DISTRO }}
- release: ${{ env.JAVA_RELEASE }}
+ distribution: ${{ env.JAVA_DISTRO }}
+ java-version: ${{ env.JAVA_RELEASE }}
cache: maven
- name: Build and deploy
env:
From cd3bd13d86f5f8c3da7ea590fb165a8802089538 Mon Sep 17 00:00:00 2001
From: Joe DiPol
Date: Wed, 22 May 2024 13:33:16 -0700
Subject: [PATCH 016/245] Fix typo in java-version for setup java action
(#8789)
---
.github/workflows/snapshotrelease.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/snapshotrelease.yaml b/.github/workflows/snapshotrelease.yaml
index b3af9e2a64e..a4be65e8357 100644
--- a/.github/workflows/snapshotrelease.yaml
+++ b/.github/workflows/snapshotrelease.yaml
@@ -29,7 +29,7 @@ jobs:
uses: actions/setup-java@v4.1.0
with:
distribution: ${{ env.JAVA_DISTRO }}
- java-version: ${{ env.JAVA_RELEASE }}
+ java-version: ${{ env.JAVA_VERSION }}
cache: maven
- name: Build and deploy
env:
From 8e33a27d581e7ad04f4ec9fc172ba59e338049c6 Mon Sep 17 00:00:00 2001
From: Joe DiPol
Date: Wed, 22 May 2024 15:39:09 -0700
Subject: [PATCH 017/245] Bump deploy plugin to 3.1.1 (#8790)
---
parent/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/parent/pom.xml b/parent/pom.xml
index d34442d75e0..3d04882637d 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -144,7 +144,7 @@
3.2.0
- 2.8.2
+ 3.1.11.63.1.11.6.13
From c68cc54e56935828dbd12dfab4b4f5d1392fc46e Mon Sep 17 00:00:00 2001
From: Joe DiPol
Date: Thu, 23 May 2024 15:12:52 -0700
Subject: [PATCH 018/245] 4.x: include SE upgrade guide in docs navbar (#8795)
* Add upgrade_4x to sitegen.yaml for MP. Remove updgrade 2.x/3.x for SE
* Fix flavor typo in upgrad_4x SE doc
---
docs/src/main/asciidoc/se/guides/upgrade_4x.adoc | 2 +-
docs/src/main/asciidoc/sitegen.yaml | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/docs/src/main/asciidoc/se/guides/upgrade_4x.adoc b/docs/src/main/asciidoc/se/guides/upgrade_4x.adoc
index c6f5b75f586..3996d6a1122 100644
--- a/docs/src/main/asciidoc/se/guides/upgrade_4x.adoc
+++ b/docs/src/main/asciidoc/se/guides/upgrade_4x.adoc
@@ -25,7 +25,7 @@ include::{rootdir}/includes/attributes.adoc[]
In Helidon 4.x we have made some major changes to Helidon. Reactive engine has been removed.
APIS and implementations are rewritten in "blocking" paradigm.
-This guide will help you upgrade a Helidon MP 3.x application to 4.x.
+This guide will help you upgrade a Helidon SE 3.x application to 4.x.
== Java 21 Runtime
diff --git a/docs/src/main/asciidoc/sitegen.yaml b/docs/src/main/asciidoc/sitegen.yaml
index ffceb56b2ec..865fd227e74 100644
--- a/docs/src/main/asciidoc/sitegen.yaml
+++ b/docs/src/main/asciidoc/sitegen.yaml
@@ -91,6 +91,7 @@ backend:
- "security-oidc.adoc"
- "tracing.adoc"
- "mp-tutorial.adoc"
+ - "upgrade_4x.adoc"
- "maven-build.adoc"
- "gradle-build.adoc"
- "graalnative.adoc"
@@ -318,8 +319,6 @@ backend:
- "metrics.adoc"
- "security-oidc.adoc"
- "tracing.adoc"
- - "upgrade.adoc"
- - "upgrade_3x.adoc"
- "upgrade_4x.adoc"
- "maven-build.adoc"
- "gradle-build.adoc"
From b218286a85c48ab3f8eeba40216b5e5750a7f8ce Mon Sep 17 00:00:00 2001
From: Thibault Vallin
Date: Fri, 24 May 2024 19:24:54 +0200
Subject: [PATCH 019/245] 4.x: Archetype - Add SLF4J dependency (#8792)
* Add SLF4J dependency
Signed-off-by: tvallin
---
.../src/main/archetype/mp/custom/database-outputs.xml | 4 ++++
.../archetypes/src/main/archetype/mp/oci/oci-mp.xml | 8 +++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/archetypes/archetypes/src/main/archetype/mp/custom/database-outputs.xml b/archetypes/archetypes/src/main/archetype/mp/custom/database-outputs.xml
index dabec091ed0..d8b91f7fad4 100644
--- a/archetypes/archetypes/src/main/archetype/mp/custom/database-outputs.xml
+++ b/archetypes/archetypes/src/main/archetype/mp/custom/database-outputs.xml
@@ -153,6 +153,10 @@
helidon-integrations-cdi-datasource-hikaricpruntime
+
diff --git a/archetypes/archetypes/src/main/archetype/mp/oci/oci-mp.xml b/archetypes/archetypes/src/main/archetype/mp/oci/oci-mp.xml
index afdb6cf6d77..041f3d2d2c4 100644
--- a/archetypes/archetypes/src/main/archetype/mp/oci/oci-mp.xml
+++ b/archetypes/archetypes/src/main/archetype/mp/oci/oci-mp.xml
@@ -1,7 +1,7 @@
-
-
-
-
io.helidon.integrations.dbojdbcruntime
+
+ io.helidon.logging
+ helidon-logging-jul
+
+
+ org.slf4j
+ slf4j-jdk14
+ org.junit.jupiter
diff --git a/tests/integration/native-image/mp-2/src/main/java/io/helidon/tests/integration/nativeimage/mp2/Mp2Main.java b/tests/integration/native-image/mp-2/src/main/java/io/helidon/tests/integration/nativeimage/mp2/Mp2Main.java
index e4f2df6ed91..f7a4f1fab32 100644
--- a/tests/integration/native-image/mp-2/src/main/java/io/helidon/tests/integration/nativeimage/mp2/Mp2Main.java
+++ b/tests/integration/native-image/mp-2/src/main/java/io/helidon/tests/integration/nativeimage/mp2/Mp2Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -16,7 +16,7 @@
package io.helidon.tests.integration.nativeimage.mp2;
import io.helidon.common.Errors;
-import io.helidon.microprofile.cdi.Main;
+import io.helidon.microprofile.server.Server;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
@@ -41,27 +41,39 @@ private Mp2Main() {
* @param args command line arguments.
*/
public static void main(final String[] args) {
- // start CDI
- Main.main(args);
+ Server server = Server.create().start();
+ boolean failed = false;
long now = System.currentTimeMillis();
- test();
+ try {
+ test(server.port());
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ failed = true;
+ }
long time = System.currentTimeMillis() - now;
System.out.println("Tests finished in " + time + " millis");
- }
- private static void test() {
- Client client = ClientBuilder.newClient();
- WebTarget target = client.target("http://localhost:8087");
+ server.stop();
+
+ if (failed) {
+ System.exit(-1);
+ }
+ }
- Errors.Collector collector = Errors.collector();
+ private static void test(int port) {
+ WebTarget target;
+ try (Client client = ClientBuilder.newClient()) {
+ target = client.target("http://localhost:" + port);
+ Errors.Collector collector = Errors.collector();
- // testing all modules
- validateJaxrs(collector, target);
- validateDb(collector, target);
+ // testing all modules
+ validateJaxrs(collector, target);
+ validateDb(collector, target);
- collector.collect()
- .checkValid();
+ collector.collect()
+ .checkValid();
+ }
}
private static void validateJaxrs(Errors.Collector collector, WebTarget target) {
diff --git a/tests/integration/native-image/mp-2/src/main/resources/hibernate.properties b/tests/integration/native-image/mp-2/src/main/resources/hibernate.properties
deleted file mode 100644
index 5d0d4b0e120..00000000000
--- a/tests/integration/native-image/mp-2/src/main/resources/hibernate.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Copyright (c) 2020 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.
-#
-
-# Byte code for JPA must be generated at compile time.
-# This is a limitation of native image
-hibernate.bytecode.provider=none
-
From 410ed2236806e724a4c3bd9b6897ded7a31b4954 Mon Sep 17 00:00:00 2001
From: Romain Grecourt
Date: Tue, 28 May 2024 11:24:01 -0700
Subject: [PATCH 024/245] Use native-image:compile-no-fork instead of
native-image:compile (#8802)
- Also update the standalone quickstarts to use the official plugin
---
applications/mp/pom.xml | 2 +-
applications/parent/pom.xml | 2 +-
applications/se/pom.xml | 2 +-
.../helidon-standalone-quickstart-mp/pom.xml | 43 +++++++++++++++++--
.../helidon-standalone-quickstart-se/pom.xml | 42 ++++++++++++++++--
5 files changed, 81 insertions(+), 10 deletions(-)
diff --git a/applications/mp/pom.xml b/applications/mp/pom.xml
index 215327acced..309ce06da94 100644
--- a/applications/mp/pom.xml
+++ b/applications/mp/pom.xml
@@ -81,7 +81,7 @@
build-native-image
- compile
+ compile-no-fork
diff --git a/applications/parent/pom.xml b/applications/parent/pom.xml
index 610ce9d2349..2369af37320 100644
--- a/applications/parent/pom.xml
+++ b/applications/parent/pom.xml
@@ -45,7 +45,7 @@
4.0.64.0.63.3.0
- 0.9.27
+ 0.10.21.5.0.Final0.6.13.3.1
diff --git a/applications/se/pom.xml b/applications/se/pom.xml
index f99de637622..5c550a6a5b0 100644
--- a/applications/se/pom.xml
+++ b/applications/se/pom.xml
@@ -70,7 +70,7 @@
build-native-image
- compile
+ compile-no-fork
diff --git a/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml b/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml
index dbb1099733b..c7972e34ecd 100644
--- a/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml
+++ b/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml
@@ -45,6 +45,7 @@
4.0.63.1.23.0.2
+ 0.10.21.5.0.Final0.5.12.7
@@ -194,6 +195,11 @@
helidon-cli-maven-plugin${version.plugin.helidon-cli}
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${version.plugin.nativeimage}
+
@@ -240,14 +246,43 @@
- io.helidon.build-tools
- helidon-maven-plugin
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${version.plugin.nativeimage}
- native-image
+ resource-config
+ package
+
+ generateResourceConfig
+
+
+
+ true
+
+
+
+ build-native-image
+ package
- native-image
+ compile-no-fork
+
+
+ true
+
+ ${project.build.outputDirectory}
+ ${project.build.finalName}
+ false
+
+ false
+
+
+
+ --add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.configure=ALL-UNNAMED
+
+
diff --git a/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml b/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml
index a6650ca559e..4c3fae982da 100644
--- a/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml
+++ b/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml
@@ -45,6 +45,7 @@
4.0.64.0.63.0.2
+ 0.10.21.5.0.Final0.5.12.7
@@ -197,6 +198,11 @@
helidon-cli-maven-plugin${version.plugin.helidon-cli}
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${version.plugin.nativeimage}
+
@@ -230,13 +236,43 @@
- io.helidon.build-tools
- helidon-maven-plugin
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${version.plugin.nativeimage}
+ resource-config
+ package
+
+ generateResourceConfig
+
+
+
+ true
+
+
+
+ build-native-image
+ package
- native-image
+ compile-no-fork
+
+
+ true
+
+ ${project.build.outputDirectory}
+ ${project.build.finalName}
+ false
+
+ false
+
+
+
+ --add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.configure=ALL-UNNAMED
+
+
From 95a4a7dc4b6641e0e75b2d99b5417ebf53d92669 Mon Sep 17 00:00:00 2001
From: Thibault Vallin
Date: Tue, 28 May 2024 20:30:43 +0200
Subject: [PATCH 025/245] Create job for native-image testing of quickstarts.
(#8719)
Signed-off-by: tvallin
---
.github/workflows/validate.yml | 11 +++++++-
etc/scripts/test-quickstarts.sh | 47 +++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+), 1 deletion(-)
create mode 100755 etc/scripts/test-quickstarts.sh
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 7e077c32807..b67d15807b3 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -88,7 +88,7 @@ jobs:
- name: Maven build
run: etc/scripts/github-build.sh
examples:
- timeout-minutes: 30
+ timeout-minutes: 40
strategy:
matrix:
os: [ ubuntu-20.04, macos-14 ]
@@ -101,11 +101,20 @@ jobs:
distribution: ${{ env.JAVA_DISTRO }}
java-version: ${{ env.JAVA_VERSION }}
cache: maven
+ - uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: 21
+ distribution: graalvm-community
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ native-image-job-reports: true
+ cache: maven
- name: Maven build
run: |
mvn -B -e "-Dmaven.test.skip=true" $MAVEN_HTTP_ARGS -DskipTests -Ppipeline install
cd examples
mvn -B verify
+ - name: Test quickstarts native image
+ run: etc/scripts/test-quickstarts.sh
mp-tck:
timeout-minutes: 60
name: "MicroProfile TCKs"
diff --git a/etc/scripts/test-quickstarts.sh b/etc/scripts/test-quickstarts.sh
new file mode 100755
index 00000000000..520a0f8617a
--- /dev/null
+++ b/etc/scripts/test-quickstarts.sh
@@ -0,0 +1,47 @@
+#!/bin/bash -e
+#
+# Copyright (c) 2024 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.
+#
+
+# Path to this script
+[ -h "${0}" ] && readonly SCRIPT_PATH="$(readlink "${0}")" || readonly SCRIPT_PATH="${0}"
+
+# Load pipeline environment setup and define WS_DIR
+. $(dirname -- "${SCRIPT_PATH}")/includes/pipeline-env.sh "${SCRIPT_PATH}" '../..'
+
+# Setup error handling using default settings (defined in includes/error_handlers.sh)
+error_trap_setup
+
+if [ -z "${GRAALVM_HOME}" ]; then
+ echo "ERROR: GRAALVM_HOME is not set";
+ exit 1
+fi
+
+if [ ! -x "${GRAALVM_HOME}/bin/native-image" ]; then
+ echo "ERROR: ${GRAALVM_HOME}/bin/native-image does not exist or is not executable";
+ exit 1
+fi
+
+mvn ${MAVEN_ARGS} --version
+
+${GRAALVM_HOME}/bin/native-image --version;
+
+# Build quickstart native-image executable and run jar file
+readonly quickstarts="helidon-quickstart-mp helidon-quickstart-se"
+for quickstart in ${quickstarts}; do
+ cd "${WS_DIR}"/examples/quickstarts/"${quickstart}"
+ mvn ${MAVEN_ARGS} -e clean install -Pnative-image -DskipTests
+ ./target/"${quickstart}" -Dexit.on.started=!
+done
From 10af0f8f6bbca1574b629eb1a8e96ea069674132 Mon Sep 17 00:00:00 2001
From: Romain Grecourt
Date: Tue, 28 May 2024 15:31:29 -0700
Subject: [PATCH 026/245] Fix native-image properties layout. (#8808)
Resolves #8755
---
.../helidon-common-key-util}/native-image.properties | 2 +-
examples/quickstarts/helidon-quickstart-mp/pom.xml | 2 +-
examples/quickstarts/helidon-quickstart-se/pom.xml | 2 +-
.../helidon-example-reflection-config.json | 0
.../helidon-standalone-quickstart-se}/native-image.properties | 2 +-
.../native-image.properties | 2 +-
.../resource-config.json | 0
.../native-image.properties | 2 +-
.../native-image.properties | 2 +-
.../helidon-microprofile-tracing/native-image.properties | 2 +-
tests/integration/native-image/mp-1/pom.xml | 2 +-
.../native-image.properties | 2 +-
tests/integration/native-image/mp-2/pom.xml | 2 +-
.../native-image.properties | 2 +-
tests/integration/native-image/mp-3/pom.xml | 2 +-
.../native-image.properties | 2 +-
tests/integration/native-image/se-1/pom.xml | 2 +-
.../helidon-tracing-providers-jaeger}/native-image.properties | 2 +-
.../helidon-tracing-providers-jaeger}/reflect-config.json | 0
.../helidon-tracing-providers-jaeger}/resource-config.json | 0
.../helidon-tracing-providers-zipkin}/native-image.properties | 2 +-
21 files changed, 17 insertions(+), 17 deletions(-)
rename common/key-util/src/main/resources/META-INF/native-image/{io.helidon.common.pki/helidon-common-pki => io.helidon.common/helidon-common-key-util}/native-image.properties (91%)
rename examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/{ => io.helidon.examples.quickstarts/helidon-standalone-quickstart-se}/helidon-example-reflection-config.json (100%)
rename examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/{ => io.helidon.examples.quickstarts/helidon-standalone-quickstart-se}/native-image.properties (91%)
rename integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/{mysql => helidon-integrations-db-mysql}/native-image.properties (91%)
rename integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/{mysql => helidon-integrations-db-mysql}/resource-config.json (100%)
rename integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/{pgsql => helidon-integrations-db-pgsql}/native-image.properties (91%)
rename integrations/microstream/cache/src/main/resources/META-INF/native-image/{ => io.helidon.integrations.microstream/helidon-integrations-microstream-cache}/native-image.properties (91%)
rename microprofile/tracing/src/main/resources/META-INF/native-image/{io.helidon.microprofile => io.helidon.microprofile.tracing}/helidon-microprofile-tracing/native-image.properties (91%)
rename tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/{helidon-tests-native-image-mp1 => helidon-tests-native-image-mp-1}/native-image.properties (91%)
rename tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/{helidon-tests-native-image-mp2 => helidon-tests-native-image-mp-2}/native-image.properties (91%)
rename tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/{helidon-tests-native-image-mp3 => helidon-tests-native-image-mp-3}/native-image.properties (91%)
rename tracing/providers/jaeger/src/main/resources/META-INF/native-image/{io.helidon.tracing/helidon-tracing-jaeger => io.helidon.tracing.providers/helidon-tracing-providers-jaeger}/native-image.properties (91%)
rename tracing/providers/jaeger/src/main/resources/META-INF/native-image/{io.helidon.tracing/helidon-tracing-jaeger => io.helidon.tracing.providers/helidon-tracing-providers-jaeger}/reflect-config.json (100%)
rename tracing/providers/jaeger/src/main/resources/META-INF/native-image/{io.helidon.tracing/helidon-tracing-jaeger => io.helidon.tracing.providers/helidon-tracing-providers-jaeger}/resource-config.json (100%)
rename tracing/providers/zipkin/src/main/resources/META-INF/native-image/{io.helidon.tracing/helidon-tracing-zipkin => io.helidon.tracing.providers/helidon-tracing-providers-zipkin}/native-image.properties (92%)
diff --git a/common/key-util/src/main/resources/META-INF/native-image/io.helidon.common.pki/helidon-common-pki/native-image.properties b/common/key-util/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-key-util/native-image.properties
similarity index 91%
rename from common/key-util/src/main/resources/META-INF/native-image/io.helidon.common.pki/helidon-common-pki/native-image.properties
rename to common/key-util/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-key-util/native-image.properties
index 432cf0699a9..59a30af9959 100644
--- a/common/key-util/src/main/resources/META-INF/native-image/io.helidon.common.pki/helidon-common-pki/native-image.properties
+++ b/common/key-util/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-key-util/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
diff --git a/examples/quickstarts/helidon-quickstart-mp/pom.xml b/examples/quickstarts/helidon-quickstart-mp/pom.xml
index 41d32b7e77d..2abc9acd050 100644
--- a/examples/quickstarts/helidon-quickstart-mp/pom.xml
+++ b/examples/quickstarts/helidon-quickstart-mp/pom.xml
@@ -27,7 +27,7 @@
4.0.0-SNAPSHOT../../../applications/mp/pom.xml
- io.helidon.examples
+ io.helidon.examples.quickstartshelidon-quickstart-mpHelidon Examples Quickstart MP
diff --git a/examples/quickstarts/helidon-quickstart-se/pom.xml b/examples/quickstarts/helidon-quickstart-se/pom.xml
index e41bf4d97e6..b8b2fc473a7 100644
--- a/examples/quickstarts/helidon-quickstart-se/pom.xml
+++ b/examples/quickstarts/helidon-quickstart-se/pom.xml
@@ -27,7 +27,7 @@
4.0.0-SNAPSHOT../../../applications/se/pom.xml
- io.helidon.examples
+ io.helidon.examples.quickstartshelidon-quickstart-se4.0.0-SNAPSHOTHelidon Examples Quickstart SE
diff --git a/examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/helidon-example-reflection-config.json b/examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/io.helidon.examples.quickstarts/helidon-standalone-quickstart-se/helidon-example-reflection-config.json
similarity index 100%
rename from examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/helidon-example-reflection-config.json
rename to examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/io.helidon.examples.quickstarts/helidon-standalone-quickstart-se/helidon-example-reflection-config.json
diff --git a/examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/native-image.properties b/examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/io.helidon.examples.quickstarts/helidon-standalone-quickstart-se/native-image.properties
similarity index 91%
rename from examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/native-image.properties
rename to examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/io.helidon.examples.quickstarts/helidon-standalone-quickstart-se/native-image.properties
index 2ef72b30947..cfcdd39853f 100644
--- a/examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/native-image.properties
+++ b/examples/quickstarts/helidon-standalone-quickstart-se/src/main/resources/META-INF/native-image/io.helidon.examples.quickstarts/helidon-standalone-quickstart-se/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2019, 2021 Oracle and/or its affiliates.
+# Copyright (c) 2019, 2024 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.
diff --git a/integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/mysql/native-image.properties b/integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-mysql/native-image.properties
similarity index 91%
rename from integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/mysql/native-image.properties
rename to integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-mysql/native-image.properties
index 931f3e4c8a3..06cd39d9081 100644
--- a/integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/mysql/native-image.properties
+++ b/integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-mysql/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2021, 2023 Oracle and/or its affiliates.
+# Copyright (c) 2021, 2024 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.
diff --git a/integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/mysql/resource-config.json b/integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-mysql/resource-config.json
similarity index 100%
rename from integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/mysql/resource-config.json
rename to integrations/db/mysql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-mysql/resource-config.json
diff --git a/integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/pgsql/native-image.properties b/integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-pgsql/native-image.properties
similarity index 91%
rename from integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/pgsql/native-image.properties
rename to integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-pgsql/native-image.properties
index c6dd7c08951..f41b18e3d2c 100644
--- a/integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/pgsql/native-image.properties
+++ b/integrations/db/pgsql/src/main/resources/META-INF/native-image/io.helidon.integrations.db/helidon-integrations-db-pgsql/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020 Oracle and/or its affiliates.
+# Copyright (c) 2020, 2024 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.
diff --git a/integrations/microstream/cache/src/main/resources/META-INF/native-image/native-image.properties b/integrations/microstream/cache/src/main/resources/META-INF/native-image/io.helidon.integrations.microstream/helidon-integrations-microstream-cache/native-image.properties
similarity index 91%
rename from integrations/microstream/cache/src/main/resources/META-INF/native-image/native-image.properties
rename to integrations/microstream/cache/src/main/resources/META-INF/native-image/io.helidon.integrations.microstream/helidon-integrations-microstream-cache/native-image.properties
index 7c9eff2c244..855627f8019 100644
--- a/integrations/microstream/cache/src/main/resources/META-INF/native-image/native-image.properties
+++ b/integrations/microstream/cache/src/main/resources/META-INF/native-image/io.helidon.integrations.microstream/helidon-integrations-microstream-cache/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2021 Oracle and/or its affiliates.
+# Copyright (c) 2021, 2024 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.
diff --git a/microprofile/tracing/src/main/resources/META-INF/native-image/io.helidon.microprofile/helidon-microprofile-tracing/native-image.properties b/microprofile/tracing/src/main/resources/META-INF/native-image/io.helidon.microprofile.tracing/helidon-microprofile-tracing/native-image.properties
similarity index 91%
rename from microprofile/tracing/src/main/resources/META-INF/native-image/io.helidon.microprofile/helidon-microprofile-tracing/native-image.properties
rename to microprofile/tracing/src/main/resources/META-INF/native-image/io.helidon.microprofile.tracing/helidon-microprofile-tracing/native-image.properties
index ef4cdca252a..ba3fa658b6a 100644
--- a/microprofile/tracing/src/main/resources/META-INF/native-image/io.helidon.microprofile/helidon-microprofile-tracing/native-image.properties
+++ b/microprofile/tracing/src/main/resources/META-INF/native-image/io.helidon.microprofile.tracing/helidon-microprofile-tracing/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
diff --git a/tests/integration/native-image/mp-1/pom.xml b/tests/integration/native-image/mp-1/pom.xml
index 8ee1ce95039..43e5e2dc21c 100644
--- a/tests/integration/native-image/mp-1/pom.xml
+++ b/tests/integration/native-image/mp-1/pom.xml
@@ -25,7 +25,7 @@
4.0.0-SNAPSHOT../../../../applications/mp/pom.xml
- io.helidon.tests.integration
+ io.helidon.tests.integration.native-imagehelidon-tests-native-image-mp-1Helidon Tests Integration GraalVM Native image MP1
diff --git a/tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp1/native-image.properties b/tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-1/native-image.properties
similarity index 91%
rename from tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp1/native-image.properties
rename to tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-1/native-image.properties
index 8b0df87467f..695c051e404 100644
--- a/tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp1/native-image.properties
+++ b/tests/integration/native-image/mp-1/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-1/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
diff --git a/tests/integration/native-image/mp-2/pom.xml b/tests/integration/native-image/mp-2/pom.xml
index 6ad69a95eab..62d211fd3b3 100644
--- a/tests/integration/native-image/mp-2/pom.xml
+++ b/tests/integration/native-image/mp-2/pom.xml
@@ -25,7 +25,7 @@
4.0.0-SNAPSHOT../../../../applications/mp/pom.xml
- io.helidon.tests.integration
+ io.helidon.tests.integration.native-imagehelidon-tests-native-image-mp-2Helidon Tests Integration GraalVM Native image MP2
diff --git a/tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp2/native-image.properties b/tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-2/native-image.properties
similarity index 91%
rename from tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp2/native-image.properties
rename to tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-2/native-image.properties
index 2ec31934688..019b20a2a29 100644
--- a/tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp2/native-image.properties
+++ b/tests/integration/native-image/mp-2/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-2/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
diff --git a/tests/integration/native-image/mp-3/pom.xml b/tests/integration/native-image/mp-3/pom.xml
index a1eb94f4b91..f54cc2d5515 100644
--- a/tests/integration/native-image/mp-3/pom.xml
+++ b/tests/integration/native-image/mp-3/pom.xml
@@ -25,7 +25,7 @@
4.0.0-SNAPSHOT../../../../applications/mp/pom.xml
- io.helidon.tests.integration
+ io.helidon.tests.integration.native-imagehelidon-tests-native-image-mp-3Helidon Tests Integration GraalVM Native image MP3
diff --git a/tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp3/native-image.properties b/tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-3/native-image.properties
similarity index 91%
rename from tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp3/native-image.properties
rename to tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-3/native-image.properties
index 6d6ffe86a3e..69b9f9e926b 100644
--- a/tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp3/native-image.properties
+++ b/tests/integration/native-image/mp-3/src/main/resources/META-INF/native-image/io.helidon.tests.integration.native-image/helidon-tests-native-image-mp-3/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
diff --git a/tests/integration/native-image/se-1/pom.xml b/tests/integration/native-image/se-1/pom.xml
index aaa5b4477c1..8c01963eb8c 100644
--- a/tests/integration/native-image/se-1/pom.xml
+++ b/tests/integration/native-image/se-1/pom.xml
@@ -25,7 +25,7 @@
4.0.0-SNAPSHOT../../../../applications/se/pom.xml
- io.helidon.tests.integration
+ io.helidon.tests.integration.native-imagehelidon-tests-native-image-se-1Helidon Tests Integration GraalVM Native image SE 1
diff --git a/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/native-image.properties b/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/native-image.properties
similarity index 91%
rename from tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/native-image.properties
rename to tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/native-image.properties
index c82e50b43c1..07c3c2bc0cb 100644
--- a/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/native-image.properties
+++ b/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2019, 2023 Oracle and/or its affiliates.
+# Copyright (c) 2019, 2024 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.
diff --git a/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/reflect-config.json b/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/reflect-config.json
similarity index 100%
rename from tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/reflect-config.json
rename to tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/reflect-config.json
diff --git a/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/resource-config.json b/tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/resource-config.json
similarity index 100%
rename from tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-jaeger/resource-config.json
rename to tracing/providers/jaeger/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-jaeger/resource-config.json
diff --git a/tracing/providers/zipkin/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-zipkin/native-image.properties b/tracing/providers/zipkin/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-zipkin/native-image.properties
similarity index 92%
rename from tracing/providers/zipkin/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-zipkin/native-image.properties
rename to tracing/providers/zipkin/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-zipkin/native-image.properties
index 0b5b28649e5..346f9b6ba6b 100644
--- a/tracing/providers/zipkin/src/main/resources/META-INF/native-image/io.helidon.tracing/helidon-tracing-zipkin/native-image.properties
+++ b/tracing/providers/zipkin/src/main/resources/META-INF/native-image/io.helidon.tracing.providers/helidon-tracing-providers-zipkin/native-image.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2019, 2023 Oracle and/or its affiliates.
+# Copyright (c) 2019, 2024 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.
From 851db73d9a8b74f0287876ef05ace86227db8cea Mon Sep 17 00:00:00 2001
From: Tim Quinn
Date: Tue, 28 May 2024 18:01:44 -0500
Subject: [PATCH 027/245] Harden WebClientSecurity against absent or disabled
tracing (#8809)
Signed-off-by: Tim Quinn
---
.../webclient/security/WebClientSecurity.java | 26 +++++++++++++++----
1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/webclient/security/src/main/java/io/helidon/webclient/security/WebClientSecurity.java b/webclient/security/src/main/java/io/helidon/webclient/security/WebClientSecurity.java
index 6efe6ad3eac..a8948062f40 100644
--- a/webclient/security/src/main/java/io/helidon/webclient/security/WebClientSecurity.java
+++ b/webclient/security/src/main/java/io/helidon/webclient/security/WebClientSecurity.java
@@ -111,11 +111,22 @@ public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest requ
context = createContext(request);
}
- Span span = context.tracer()
- .spanBuilder("security:outbound")
- .parent(context.tracingSpan())
- .start();
+ Tracer tracer = context.tracer();
+ Span span;
+ if (tracer == null) {
+ span = null;
+ } else {
+ SpanContext parentSpanContext = context.tracingSpan();
+ span = tracer.spanBuilder("security:outbound")
+ .update(builder -> {
+ if (parentSpanContext != null) {
+ builder.parent(parentSpanContext);
+ }
+ }
+ )
+ .start();
+ }
String explicitProvider = request.properties().get(PROVIDER_NAME);
OutboundSecurityClientBuilder clientBuilder;
@@ -194,7 +205,9 @@ private WebClientServiceResponse processResponse(WebClientServiceRequest request
HeaderName headerName = HeaderNames.create(entry.getKey());
clientHeaders.set(headerName, entry.getValue().toArray(new String[0]));
}
- span.end();
+ if (span != null) {
+ span.end();
+ }
return chain.proceed(request);
} catch (Exception e) {
traceError(span, e, null);
@@ -216,6 +229,9 @@ private SecurityContext createContext(WebClientServiceRequest request) {
static void traceError(Span span, Throwable throwable, String description) {
// failed
+ if (span == null) {
+ return;
+ }
span.status(Span.Status.ERROR);
if (throwable == null) {
From 15fb498623635f9fb8a3dbc83796f790017e20ee Mon Sep 17 00:00:00 2001
From: Tomas Langer
Date: Wed, 29 May 2024 13:53:39 +0200
Subject: [PATCH 028/245] Service Registry (#8766)
* Support for inherited annotations in types.
* Support for text resources in CodegenFiler, that supports replacing content in annotation processor (to augment or replace resources).
* Introduction of Helidon Service Registry.
* Replace usage of Helidon Inject with Service Registry
* Remove usage of Helidon Inject from config, webserver, webclient, tls, and fault tolerance
* OCI module based on service registry
* Support for config factory methods in custom methods for builder (instead just on Blueprint)
* OCI integration using Service Registry - support for Region and Authentication Details Provider services.
* Refactor TLS certificates to use Service Registry instead of Helidon Inject.
* Move secrets config sources to correct directories.
* Refactor secrets config sources to use Service Registry (from new `helidon-integrations-oci` module)
* Use `helidon.oci` to configure metadata to connect to OCI
* Introducing support for service registry in blueprints (to load providers).
* A fix for discovered issue with just a single implementation loaded for each service loader service.
* Support custom IMDS URI.
* Support for configurable timeout for calls to IMDS (with a non-zero default).
* Fix tests to work on a machine that has oci config
* Check IMDS availability before reading region from it.
* Use configured metadata service URI for Region provider as well.
* Deprecate OciConfig from OCI SDK Runtime
Signed-off-by: Tomas Langer
---
all/pom.xml | 12 +
bom/pom.xml | 17 +
.../io/helidon/builder/api/Prototype.java | 28 +
.../builder/codegen/BuilderCodegen.java | 33 +
.../builder/codegen/FactoryMethods.java | 20 +-
.../codegen/GenerateAbstractBuilder.java | 238 +++++-
.../helidon/builder/codegen/TypeContext.java | 8 +-
.../io/helidon/builder/codegen/Types.java | 4 +
builder/tests/builder/pom.xml | 4 +
.../WithProviderRegistryBlueprint.java | 71 ++
.../builder/src/main/java/module-info.java | 3 +-
.../builder/test/ProviderRegistryTest.java | 172 ++++
builder/tests/codegen/pom.xml | 68 ++
.../io/helidon/builder/codegen/TypesTest.java | 153 ++++
.../io/helidon/common/types/Annotated.java | 33 +-
.../common/types/AnnotationBlueprint.java | 46 +-
.../common/types/TypeInfoBlueprint.java | 7 +-
.../common/types/TypeNameBlueprint.java | 11 +-
.../helidon/common/types/TypeNameSupport.java | 53 +-
.../io/helidon/common/types/TypeNames.java | 63 +-
.../types/TypedElementInfoBlueprint.java | 8 +-
builder/tests/pom.xml | 1 +
.../java/io/helidon/codegen/apt/AptFiler.java | 24 +
.../codegen/apt/AptTypeInfoFactory.java | 188 +++-
.../codegen/apt/FilerTextResourceImpl.java | 96 +++
.../codegen/classmodel/ContentSupport.java | 6 +
.../java/io/helidon/codegen/CodegenFiler.java | 67 +-
.../io/helidon/codegen/FilerTextResource.java | 44 +
common/tls/pom.xml | 15 +-
.../io/helidon/common/tls/TlsManager.java | 6 +-
common/tls/src/main/java/module-info.java | 3 +-
.../io/helidon/common/types/Annotated.java | 33 +-
.../io/helidon/common/types/TypeInfo.java | 139 ++-
.../io/helidon/common/types/TypeNames.java | 5 +
.../common/types/TypedElementInfo.java | 141 ++-
config/config/pom.xml | 44 +-
.../main/java/io/helidon/config/Config.java | 5 +-
...onfigProducer.java => ConfigProvider.java} | 40 +-
.../java/io/helidon/config/MetaConfig.java | 17 +-
.../io/helidon/config/spi/ConfigFilter.java | 2 +
.../config/spi/ConfigMapperProvider.java | 4 +-
.../io/helidon/config/spi/ConfigParser.java | 4 +-
.../io/helidon/config/spi/ConfigSource.java | 6 +-
config/config/src/main/java/module-info.java | 11 +-
.../resources/META-INF/helidon/service.loader | 4 +
.../tests/config-metadata-builder-api/pom.xml | 14 +-
config/tests/service-registry/pom.xml | 42 +-
.../service/registry/ConfigProducerTest.java | 37 +-
.../service/registry/TestConfigSource.java | 10 +-
.../src/test/resources/application.yaml | 7 +-
etc/checkstyle-suppressions.xml | 5 +-
etc/copyright-exclude.txt | 1 +
examples/inject/README.md | 16 -
examples/inject/basics/README.md | 30 -
examples/inject/basics/pom.xml | 89 --
.../examples/inject/basics/LittleHammer.java | 30 -
.../helidon/examples/inject/basics/Main.java | 61 --
.../examples/inject/basics/ToolBox.java | 97 ---
.../src/main/resources/logging.properties | 26 -
examples/inject/configdriven/README.md | 28 -
examples/inject/configdriven/pom.xml | 113 ---
.../examples/inject/configdriven/Drill.java | 48 --
.../configdriven/DrillConfigBlueprint.java | 31 -
.../examples/inject/configdriven/Main.java | 55 --
.../src/main/resources/logging.properties | 26 -
examples/inject/interceptors/README.md | 21 -
examples/inject/interceptors/pom.xml | 94 --
.../examples/inject/interceptors/Main.java | 47 -
.../inject/interceptors/TurnInterceptor.java | 46 -
.../inject/interceptors/TurningTool.java | 27 -
.../src/main/resources/logging.properties | 26 -
examples/inject/pom.xml | 40 -
examples/inject/providers/README.md | 38 -
examples/inject/providers/pom.xml | 96 ---
.../inject/providers/AngleGrinderSaw.java | 48 --
.../inject/providers/BladeProvider.java | 78 --
.../inject/providers/CircularSaw.java | 49 --
.../examples/inject/providers/HandSaw.java | 47 -
.../examples/inject/providers/Main.java | 52 --
.../examples/inject/providers/Nail.java | 32 -
.../examples/inject/providers/NailGun.java | 55 --
.../inject/providers/NailProvider.java | 41 -
.../examples/inject/providers/Saw.java | 31 -
.../examples/inject/providers/SizedBlade.java | 47 -
.../examples/inject/providers/TableSaw.java | 50 --
.../src/main/resources/logging.properties | 26 -
.../inject/providers/ProvidersTest.java | 33 -
examples/pom.xml | 1 -
fault-tolerance/fault-tolerance/pom.xml | 44 -
.../java/io/helidon/faulttolerance/Async.java | 4 +-
.../io/helidon/faulttolerance/AsyncImpl.java | 30 +-
.../io/helidon/faulttolerance/Bulkhead.java | 4 +-
.../BulkheadConfigBlueprint.java | 2 -
.../helidon/faulttolerance/BulkheadImpl.java | 5 +-
.../faulttolerance/CircuitBreaker.java | 4 +-
.../CircuitBreakerConfigBlueprint.java | 2 -
.../faulttolerance/CircuitBreakerImpl.java | 8 +-
.../java/io/helidon/faulttolerance/Retry.java | 4 +-
.../io/helidon/faulttolerance/RetryImpl.java | 5 +-
.../io/helidon/faulttolerance/Timeout.java | 4 +-
.../helidon/faulttolerance/TimeoutImpl.java | 5 +-
.../src/main/java/module-info.java | 12 -
.../inject/tests/inject/tbox/ToolBoxTest.java | 13 +-
.../HelloInjectionWorldSanityTest.java | 8 +-
.../graal/native-image-extension/pom.xml | 4 +
.../extension/HelidonReflectionFeature.java | 72 +-
.../nativeimage/extension/NativeUtil.java | 21 +-
.../src/main/java/module-info.java | 3 +-
integrations/oci/README.md | 4 +
integrations/oci/oci/README.md | 43 +
integrations/oci/oci/pom.xml | 141 +++
.../integrations/oci/AtnDetailsProvider.java | 81 ++
.../integrations/oci/AtnStrategyConfig.java | 94 ++
.../oci/AtnStrategyConfigFile.java | 85 ++
.../oci/AtnStrategyInstancePrincipal.java | 97 +++
.../oci/AtnStrategyResourcePrincipal.java | 80 ++
.../ConfigFileStrategyConfigBlueprint.java | 43 +
.../oci/ConfigStrategyConfigBlueprint.java | 96 +++
.../integrations/oci/OciConfigBlueprint.java | 136 +++
.../integrations/oci/OciConfigProvider.java | 77 ++
.../integrations/oci/OciConfigSupport.java | 41 +
.../integrations/oci/RegionProvider.java | 47 +
.../oci/RegionProviderAtnStrategy.java | 55 ++
.../oci/RegionProviderConfig.java | 35 +-
.../integrations/oci/RegionProviderSdk.java | 62 ++
.../integrations/oci}/package-info.java | 7 +-
.../integrations/oci/spi/OciAtnStrategy.java | 54 ++
.../integrations/oci/spi/OciRegion.java | 48 ++
.../integrations/oci/spi/package-info.java | 23 +
.../oci/oci/src/main/java/module-info.java | 57 ++
.../integrations/oci/OciIntegrationTest.java | 199 +++++
.../oci/src/test/resources/test-oci-config | 23 +-
integrations/oci/pom.xml | 5 +-
.../OciAuthenticationDetailsProvider.java | 4 +-
.../oci/sdk/runtime/OciAvailability.java | 4 +-
.../oci/sdk/runtime/OciConfigBlueprint.java | 4 +-
.../oci/sdk/runtime/OciExtension.java | 5 +-
.../runtime/src/main/java/module-info.java | 5 +-
.../oci/sdk/tests/test-application/pom.xml | 29 -
.../oci/sdk/tests/test-module1/pom.xml | 14 -
.../oci/sdk/tests/test-module2/pom.xml | 14 -
.../pom.xml | 9 +-
.../AbstractSecretBundleConfigSource.java | 80 +-
.../OciSecretsConfigSourceProvider.java | 38 +-
.../SecretBundleLazyConfigSource.java | 146 +---
.../SecretBundleNodeConfigSource.java | 365 +++-----
.../secrets/configsource/package-info.java | 2 +-
.../src/main/java/module-info.java | 8 +-
.../secrets/configsource/IsModifiedTest.java | 2 +-
.../oci/secrets/configsource/UsageTest.java | 3 +-
.../secrets/configsource/ValueNodeTest.java | 2 +-
.../src/test/java/logging.properties | 8 +-
.../src/test/resources/meta-config.yaml | 3 +-
.../pom.xml | 8 +-
.../OciSecretsMpMetaConfigProvider.java | 1 -
.../secrets/mp/configsource/package-info.java | 2 +-
.../src/main/java/module-info.java | 2 +-
.../secrets/mp/configsource/UsageTest.java | 3 +-
.../src/test/java/logging.properties | 2 +-
.../src/test/resources/mp-meta-config.yaml | 3 +-
integrations/oci/tls-certificates/README.md | 5 +-
integrations/oci/tls-certificates/pom.xml | 158 ++--
.../DefaultOciCertificatesDownloader.java | 26 +-
.../DefaultOciCertificatesTlsManager.java | 43 +-
...aultOciCertificatesTlsManagerProvider.java | 3 +-
.../DefaultOciPrivateKeyDownloader.java | 14 +-
.../oci/tls/certificates/LifecycleHook.java | 32 -
...CertificatesTlsManagerConfigBlueprint.java | 23 +-
.../spi/OciCertificatesDownloader.java | 6 +-
.../spi/OciPrivateKeyDownloader.java | 6 +-
.../src/main/java/module-info.java | 13 +-
.../OciCertificatesTlsManagerTest.java | 48 +-
.../TestOciCertificatesDownloader.java | 28 +-
.../TestOciPrivateKeyDownloader.java | 21 +-
.../tls/certificates/TestingCdiExtension.java | 71 --
.../resources/{oci.yaml => oci-config.yaml} | 5 +-
.../cdi/BuildTimeInitializer.java | 11 +-
pom.xml | 1 +
service/README.md | 93 ++
service/codegen/pom.xml | 55 ++
.../service/codegen/DescriptorClassCode.java | 55 ++
.../codegen/DescriptorClassCodeImpl.java | 20 +-
.../codegen/GenerateServiceDescriptor.java | 801 ++++++++++++++++++
.../codegen/HelidonMetaInfServices.java | 186 ++++
.../service/codegen/ParamDefinition.java | 70 ++
.../codegen/RegistryCodegenContext.java | 133 +++
.../codegen/RegistryCodegenContextImpl.java | 134 +++
.../codegen/RegistryCodegenExtension.java | 23 +-
.../service/codegen/RegistryRoundContext.java | 61 ++
.../service/codegen/RoundContextImpl.java | 89 ++
.../service/codegen/ServiceCodegenTypes.java | 58 ++
.../service/codegen/ServiceExtension.java | 46 +
.../service/codegen/ServiceOptions.java | 35 +
.../ServiceRegistryCodegenExtension.java | 281 ++++++
.../ServiceRegistryCodegenProvider.java | 72 ++
.../io/helidon/service/codegen/SuperType.java | 36 +
.../service/codegen/TypedElements.java | 95 +++
.../service/codegen}/package-info.java | 6 +-
.../codegen/src/main/java/module-info.java | 23 +-
service/pom.xml | 63 ++
service/registry/etc/spotbugs/exclude.xml | 31 +
service/registry/pom.xml | 105 +++
.../registry/CoreServiceDiscovery.java | 250 ++++++
.../service/registry/CoreServiceRegistry.java | 332 ++++++++
.../registry/CoreServiceRegistryManager.java | 72 ++
.../service/registry/DependencyBlueprint.java | 89 ++
.../service/registry/DependencyContext.java | 48 ++
.../registry/DependencyContextImpl.java | 27 +-
.../service/registry/DescriptorMetadata.java | 68 ++
.../service/registry/GeneratedService.java | 354 ++++++++
.../registry/GlobalServiceRegistry.java | 118 +++
.../io/helidon/service/registry/Service.java | 157 ++++
.../service/registry/ServiceDiscovery.java | 74 ++
.../helidon/service/registry/ServiceInfo.java | 72 ++
.../ServiceLoader__ServiceDescriptor.java | 123 +++
.../service/registry/ServiceMetadata.java | 21 +-
.../service/registry/ServiceRegistry.java | 209 +++++
.../ServiceRegistryConfigBlueprint.java | 86 ++
.../ServiceRegistryConfigSupport.java | 115 +++
.../registry/ServiceRegistryException.java | 41 +
.../registry/ServiceRegistryManager.java | 57 ++
.../ServiceRegistryManagerDiscovery.java | 46 +
.../ServiceRegistry__ServiceDescriptor.java | 53 ++
.../service/registry/package-info.java | 23 +
.../spi/ServiceRegistryManagerProvider.java | 42 +
.../service/registry/spi}/package-info.java | 6 +-
.../registry/src/main/java/module-info.java | 29 +
.../native-image.properties | 19 +
.../resource-config.json | 10 +
service/tests/codegen/pom.xml | 64 ++
.../codegen/src/main/java/module-info.java | 14 +-
.../codegen/ServiceCodegenTypesTest.java | 94 ++
.../codegen/src/test/java/module-info.java | 22 +-
service/tests/pom.xml | 51 ++
service/tests/registry/pom.xml | 110 +++
.../service/test/registry/MyContract.java | 11 +-
.../service/test/registry/MyService.java | 21 +-
.../service/test/registry/MyService2.java | 24 +-
.../registry/src/main/java/module-info.java | 11 +-
.../test/registry/CyclicDependencyTest.java | 148 ++++
.../test/registry/RegistryConfigTest.java | 61 ++
.../service/test/registry/RegistryTest.java | 82 ++
tests/integration/native-image/mp-1/pom.xml | 5 -
.../mp-1/src/main/java/module-info.java | 5 +-
.../native-image.properties | 5 +-
tests/integration/native-image/mp-3/pom.xml | 5 -
tests/integration/vault/pom.xml | 5 +
webclient/api/pom.xml | 21 -
.../io/helidon/webclient/api/LoomClient.java | 7 +-
.../api/WebClientConfigBlueprint.java | 2 -
webclient/api/src/main/java/module-info.java | 3 -
webclient/http1/pom.xml | 20 -
webserver/webserver/pom.xml | 5 -
.../webserver/WebServerConfigDrivenTest.java | 69 --
254 files changed, 9653 insertions(+), 3239 deletions(-)
create mode 100644 builder/tests/builder/src/main/java/io/helidon/builder/test/testsubjects/WithProviderRegistryBlueprint.java
create mode 100644 builder/tests/builder/src/test/java/io/helidon/builder/test/ProviderRegistryTest.java
create mode 100644 builder/tests/codegen/pom.xml
create mode 100644 builder/tests/codegen/src/test/java/io/helidon/builder/codegen/TypesTest.java
create mode 100644 codegen/apt/src/main/java/io/helidon/codegen/apt/FilerTextResourceImpl.java
create mode 100644 codegen/codegen/src/main/java/io/helidon/codegen/FilerTextResource.java
rename config/config/src/main/java/io/helidon/config/{ConfigProducer.java => ConfigProvider.java} (68%)
create mode 100644 config/config/src/main/resources/META-INF/helidon/service.loader
delete mode 100644 examples/inject/README.md
delete mode 100644 examples/inject/basics/README.md
delete mode 100644 examples/inject/basics/pom.xml
delete mode 100644 examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/LittleHammer.java
delete mode 100644 examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Main.java
delete mode 100644 examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/ToolBox.java
delete mode 100644 examples/inject/basics/src/main/resources/logging.properties
delete mode 100644 examples/inject/configdriven/README.md
delete mode 100644 examples/inject/configdriven/pom.xml
delete mode 100644 examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Drill.java
delete mode 100644 examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/DrillConfigBlueprint.java
delete mode 100644 examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Main.java
delete mode 100644 examples/inject/configdriven/src/main/resources/logging.properties
delete mode 100644 examples/inject/interceptors/README.md
delete mode 100644 examples/inject/interceptors/pom.xml
delete mode 100644 examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Main.java
delete mode 100644 examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurnInterceptor.java
delete mode 100644 examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurningTool.java
delete mode 100644 examples/inject/interceptors/src/main/resources/logging.properties
delete mode 100644 examples/inject/pom.xml
delete mode 100644 examples/inject/providers/README.md
delete mode 100644 examples/inject/providers/pom.xml
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/AngleGrinderSaw.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/BladeProvider.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/CircularSaw.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/HandSaw.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Main.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Nail.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailGun.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailProvider.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Saw.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/SizedBlade.java
delete mode 100644 examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/TableSaw.java
delete mode 100644 examples/inject/providers/src/main/resources/logging.properties
delete mode 100644 examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/ProvidersTest.java
create mode 100644 integrations/oci/README.md
create mode 100644 integrations/oci/oci/README.md
create mode 100644 integrations/oci/oci/pom.xml
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnDetailsProvider.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfig.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfigFile.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyInstancePrincipal.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileStrategyConfigBlueprint.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigStrategyConfigBlueprint.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigBlueprint.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigProvider.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigSupport.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProvider.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAtnStrategy.java
rename examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Hammer.java => integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderConfig.java (50%)
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderSdk.java
rename {examples/inject/providers/src/main/java/io/helidon/examples/inject/providers => integrations/oci/oci/src/main/java/io/helidon/integrations/oci}/package-info.java (74%)
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciAtnStrategy.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciRegion.java
create mode 100644 integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/package-info.java
create mode 100644 integrations/oci/oci/src/main/java/module-info.java
create mode 100644 integrations/oci/oci/src/test/java/io/helidon/integrations/oci/OciIntegrationTest.java
rename examples/inject/configdriven/src/main/resources/application.yaml => integrations/oci/oci/src/test/resources/test-oci-config (63%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/pom.xml (94%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java (70%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java (86%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java (78%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java (76%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java (95%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/main/java/module-info.java (83%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java (97%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java (97%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java (96%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/test/java/logging.properties (84%)
rename integrations/oci/{oci-secrets-config-source => secrets-config-source}/src/test/resources/meta-config.yaml (90%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/pom.xml (93%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java (99%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java (95%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/src/main/java/module-info.java (96%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java (97%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/src/test/java/logging.properties (92%)
rename integrations/oci/{oci-secrets-mp-config-source => secrets-mp-config-source}/src/test/resources/mp-meta-config.yaml (91%)
delete mode 100644 integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/LifecycleHook.java
delete mode 100644 integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestingCdiExtension.java
rename integrations/oci/tls-certificates/src/test/resources/{oci.yaml => oci-config.yaml} (76%)
create mode 100644 service/README.md
create mode 100644 service/codegen/pom.xml
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCode.java
rename examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Little.java => service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCodeImpl.java (57%)
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/GenerateServiceDescriptor.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/HelidonMetaInfServices.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/ParamDefinition.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContext.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContextImpl.java
rename examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Tool.java => service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenExtension.java (56%)
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/RegistryRoundContext.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/RoundContextImpl.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/ServiceCodegenTypes.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/ServiceExtension.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/ServiceOptions.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenExtension.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenProvider.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/SuperType.java
create mode 100644 service/codegen/src/main/java/io/helidon/service/codegen/TypedElements.java
rename {examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven => service/codegen/src/main/java/io/helidon/service/codegen}/package-info.java (80%)
rename examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Blade.java => service/codegen/src/main/java/module-info.java (53%)
create mode 100644 service/pom.xml
create mode 100644 service/registry/etc/spotbugs/exclude.xml
create mode 100644 service/registry/pom.xml
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/CoreServiceDiscovery.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistry.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistryManager.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/DependencyBlueprint.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/DependencyContext.java
rename examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/ScrewDriver.java => service/registry/src/main/java/io/helidon/service/registry/DependencyContextImpl.java (55%)
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/DescriptorMetadata.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/GeneratedService.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/GlobalServiceRegistry.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/Service.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceDiscovery.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceInfo.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceLoader__ServiceDescriptor.java
rename examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Big.java => service/registry/src/main/java/io/helidon/service/registry/ServiceMetadata.java (63%)
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigBlueprint.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigSupport.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryException.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManager.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManagerDiscovery.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry__ServiceDescriptor.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/package-info.java
create mode 100644 service/registry/src/main/java/io/helidon/service/registry/spi/ServiceRegistryManagerProvider.java
rename {examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors => service/registry/src/main/java/io/helidon/service/registry/spi}/package-info.java (76%)
create mode 100644 service/registry/src/main/java/module-info.java
create mode 100644 service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/native-image.properties
create mode 100644 service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/resource-config.json
create mode 100644 service/tests/codegen/pom.xml
rename examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Turn.java => service/tests/codegen/src/main/java/module-info.java (72%)
create mode 100644 service/tests/codegen/src/test/java/io/helidon/service/tests/codegen/ServiceCodegenTypesTest.java
rename examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/BigHammer.java => service/tests/codegen/src/test/java/module-info.java (58%)
create mode 100644 service/tests/pom.xml
create mode 100644 service/tests/registry/pom.xml
rename examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/Wrench.java => service/tests/registry/src/main/java/io/helidon/service/test/registry/MyContract.java (73%)
rename examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/AllenWrench.java => service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService.java (64%)
rename examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/StandardNail.java => service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService2.java (55%)
rename examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/package-info.java => service/tests/registry/src/main/java/module-info.java (75%)
create mode 100644 service/tests/registry/src/test/java/io/helidon/service/test/registry/CyclicDependencyTest.java
create mode 100644 service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryConfigTest.java
create mode 100644 service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryTest.java
delete mode 100644 webserver/webserver/src/test/java/io/helidon/webserver/WebServerConfigDrivenTest.java
diff --git a/all/pom.xml b/all/pom.xml
index 6ea7065eb75..12e171fdcd0 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -637,6 +637,10 @@
io.helidon.integrations.micrometerhelidon-integrations-micrometer-cdi
+
+ io.helidon.integrations.oci
+ helidon-integrations-oci
+ io.helidon.integrations.oci.sdkhelidon-integrations-oci-sdk-cdi
@@ -1050,6 +1054,14 @@
io.helidon.inject.configdrivenhelidon-inject-configdriven-processor
+
+ io.helidon.service
+ helidon-service-registry
+
+
+ io.helidon.service
+ helidon-service-codegen
+ io.helidon.integrations.oci.sdkhelidon-integrations-oci-sdk-processor
diff --git a/bom/pom.xml b/bom/pom.xml
index 75a5fd3aa6e..ffd88f9a7e4 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -847,6 +847,11 @@
helidon-integrations-micrometer-cdi${helidon.version}
+
+ io.helidon.integrations.oci
+ helidon-integrations-oci
+ ${helidon.version}
+ io.helidon.integrations.oci.sdkhelidon-integrations-oci-sdk-cdi
@@ -1379,6 +1384,18 @@
${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-registry
+ ${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-codegen
+ ${helidon.version}
+
+
io.helidon.integrations.oci.sdk
diff --git a/builder/api/src/main/java/io/helidon/builder/api/Prototype.java b/builder/api/src/main/java/io/helidon/builder/api/Prototype.java
index 484982f5eb3..7c8cccd86c0 100644
--- a/builder/api/src/main/java/io/helidon/builder/api/Prototype.java
+++ b/builder/api/src/main/java/io/helidon/builder/api/Prototype.java
@@ -164,6 +164,8 @@ public interface ConfiguredBuilder extends Builder {
/**
* Add additional interfaces to implement by the prototype. Provide correct types (fully qualified) for generics.
*/
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.CLASS)
public @interface Implement {
/**
* Interfaces to implement, such as {@code java.lang.Comparable}.
@@ -451,5 +455,29 @@ public interface OptionDecorator {
String[] value();
}
+ /**
+ * Generate support for Service Registry ({@code helidon-service-registry}) for any option that is annotated
+ * as {@link io.helidon.builder.api.Option.Provider}.
+ * When enabled (i.e. when this annotation is present), the service registry would be used to discover any service
+ * implementations including implementations discovered by service loader - appropriate {@code service.loader}
+ * metadata file will be generated for the service provider interfaces.
+ *
+ * Simply add this annotation to your {@link io.helidon.builder.api.Prototype.Blueprint} type to enable service registry.
+ * Note that if this annotation is NOT present, service registry would not be used, and {@link java.util.ServiceLoader}
+ * is used instead.
+ *
+ * When using this annotation, you cannot use {@code serviceRegistry} as a custom option in your blueprint, as it will
+ * be added by the annotation processor, to allow customization of the registry instance.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.CLASS)
+ public @interface RegistrySupport {
+ /**
+ * Whether to enable (default) or disable (set to {@code false}) registry support for this blueprint.
+ *
+ * @return whether the registry support should be enabled
+ */
+ boolean value() default true;
+ }
}
diff --git a/builder/codegen/src/main/java/io/helidon/builder/codegen/BuilderCodegen.java b/builder/codegen/src/main/java/io/helidon/builder/codegen/BuilderCodegen.java
index 778058532b0..c8a256d1b18 100644
--- a/builder/codegen/src/main/java/io/helidon/builder/codegen/BuilderCodegen.java
+++ b/builder/codegen/src/main/java/io/helidon/builder/codegen/BuilderCodegen.java
@@ -22,13 +22,16 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.TreeSet;
import java.util.stream.Collectors;
import io.helidon.builder.codegen.ValidationTask.ValidateConfiguredType;
import io.helidon.codegen.CodegenContext;
import io.helidon.codegen.CodegenEvent;
import io.helidon.codegen.CodegenException;
+import io.helidon.codegen.CodegenFiler;
import io.helidon.codegen.CodegenUtil;
+import io.helidon.codegen.FilerTextResource;
import io.helidon.codegen.RoundContext;
import io.helidon.codegen.classmodel.ClassModel;
import io.helidon.codegen.classmodel.Javadoc;
@@ -51,6 +54,8 @@ class BuilderCodegen implements CodegenExtension {
private final Set runtimeTypes = new HashSet<>();
// all blueprint types (for validation)
private final Set blueprintTypes = new HashSet<>();
+ // all types from service loader that should be supported by ServiceRegistry
+ private final Set serviceLoaderContracts = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
private final CodegenContext ctx;
@@ -83,6 +88,9 @@ public void process(RoundContext roundContext) {
public void processingOver(RoundContext roundContext) {
process(roundContext);
+ // now create service.loader
+ updateServiceLoaderResource();
+
// we must collect validation information after all types are generated - so
// we also listen on @Generated, so there is another round of annotation processing where we have all
// types nice and ready
@@ -114,6 +122,23 @@ public void processingOver(RoundContext roundContext) {
}
}
+ private void updateServiceLoaderResource() {
+ CodegenFiler filer = ctx.filer();
+ FilerTextResource serviceLoaderResource = filer.textResource("META-INF/helidon/service.loader");
+ List lines = new ArrayList<>(serviceLoaderResource.lines());
+ if (lines.isEmpty()) {
+ lines.add("# List of service contracts we want to support either from service registry, or from service loader");
+ }
+ for (String serviceLoaderContract : this.serviceLoaderContracts) {
+ if (!lines.contains(serviceLoaderContract)) {
+ lines.add(serviceLoaderContract);
+ }
+ }
+
+ serviceLoaderResource.lines(lines);
+ serviceLoaderResource.write();
+ }
+
private void process(RoundContext roundContext, TypeInfo blueprint) {
TypeContext typeContext = TypeContext.create(ctx, blueprint);
AnnotationDataBlueprint blueprintDef = typeContext.blueprintData();
@@ -227,6 +252,14 @@ private void process(RoundContext roundContext, TypeInfo blueprint) {
classModel,
blueprint.typeName(),
blueprint.originatingElement().orElse(blueprint.typeName()));
+
+ if (typeContext.typeInfo().supportsServiceRegistry() && typeContext.propertyData().hasProvider()) {
+ for (PrototypeProperty property : typeContext.propertyData().properties()) {
+ if (property.configuredOption().provider()) {
+ this.serviceLoaderContracts.add(property.configuredOption().providerType().genericTypeName().fqName());
+ }
+ }
+ }
}
private static void addCreateDefaultMethod(AnnotationDataBlueprint blueprintDef,
diff --git a/builder/codegen/src/main/java/io/helidon/builder/codegen/FactoryMethods.java b/builder/codegen/src/main/java/io/helidon/builder/codegen/FactoryMethods.java
index 7f33c720c30..cfd9931b2c3 100644
--- a/builder/codegen/src/main/java/io/helidon/builder/codegen/FactoryMethods.java
+++ b/builder/codegen/src/main/java/io/helidon/builder/codegen/FactoryMethods.java
@@ -151,12 +151,30 @@ private static Optional createFromConfigMethod(CodegenContext ctx
// first look at declared type and blueprint
String methodName = "create" + capitalize(typeHandler.name());
+
+ // check the blueprint itself, and then check the custom methods type
Optional returnType = findFactoryMethodByParamType(blueprint,
COMMON_CONFIG,
methodName);
+ TypeName typeWithFactoryMethod = blueprint.typeName();
+
+ if (returnType.isEmpty()) {
+ if (blueprint.hasAnnotation(Types.PROTOTYPE_CUSTOM_METHODS)) {
+ Optional typeInfo = blueprint.annotation(Types.PROTOTYPE_CUSTOM_METHODS)
+ .typeValue()
+ .flatMap(ctx::typeInfo);
+ if (typeInfo.isPresent()) {
+ TypeInfo customMethods = typeInfo.get();
+ typeWithFactoryMethod = customMethods.typeName();
+ returnType = findFactoryMethodByParamType(customMethods,
+ COMMON_CONFIG,
+ methodName);
+ }
+ }
+ }
+
if (returnType.isPresent()) {
- TypeName typeWithFactoryMethod = blueprint.typeName();
return Optional.of(new FactoryMethod(typeWithFactoryMethod,
returnType.get(),
methodName,
diff --git a/builder/codegen/src/main/java/io/helidon/builder/codegen/GenerateAbstractBuilder.java b/builder/codegen/src/main/java/io/helidon/builder/codegen/GenerateAbstractBuilder.java
index 1ae21fd766a..27a75cd7f0e 100644
--- a/builder/codegen/src/main/java/io/helidon/builder/codegen/GenerateAbstractBuilder.java
+++ b/builder/codegen/src/main/java/io/helidon/builder/codegen/GenerateAbstractBuilder.java
@@ -220,6 +220,12 @@ private static void builderMethods(InnerClass.Builder classBuilder, TypeContext
}
TypeName returnType = TypeName.createFromGenericDeclaration("BUILDER");
+
+ if (typeContext.propertyData().hasProvider() && typeContext.typeInfo().supportsServiceRegistry()) {
+ // generate setter for service registry
+ serviceRegistrySetter(classBuilder);
+ }
+
// first setters
for (PrototypeProperty child : properties) {
if (isConfigProperty(child)) {
@@ -288,6 +294,37 @@ String host() {
}
}
+ private static void serviceRegistrySetter(InnerClass.Builder classBuilder) {
+ /*
+ public BUILDER config(Config config) {
+ this.config = config;
+ config.get("server").as(String.class).ifPresent(this::server);
+ return self();
+ }
+ */
+ Javadoc javadoc = Javadoc.builder()
+ .addLine("Provide an explicit registry instance to use. ")
+ .addLine("")
+ .addLine("If not configured, the {@link "
+ + Types.GLOBAL_SERVICE_REGISTRY.fqName()
+ + "} would be used to discover services.")
+ .build();
+
+ Method.Builder builder = Method.builder()
+ .name("serviceRegistry")
+ .javadoc(javadoc)
+ .returnType(TypeArgument.create("BUILDER"), "updated builder instance")
+ .addParameter(param -> param.name("registry")
+ .type(Types.SERVICE_REGISTRY)
+ .description("service registry instance"))
+ .addContent(Objects.class)
+ .addContentLine(".requireNonNull(registry);")
+ .addContentLine("this.serviceRegistry = registry;")
+ .addContentLine("return self();");
+
+ classBuilder.addMethod(builder);
+ }
+
private static void createConfigMethod(InnerClass.Builder classBuilder, TypeContext typeContext,
AnnotationDataConfigured configured,
List properties) {
@@ -474,6 +511,11 @@ private static void fields(InnerClass.Builder classBuilder, TypeContext typeCont
if (isBuilder && (typeContext.configuredData().configured() || hasConfig(typeContext.propertyData().properties()))) {
classBuilder.addField(builder -> builder.type(Types.COMMON_CONFIG).name("config"));
}
+ if (isBuilder
+ && typeContext.typeInfo().supportsServiceRegistry()
+ && typeContext.propertyData().hasProvider()) {
+ classBuilder.addField(builder -> builder.type(Types.SERVICE_REGISTRY).name("serviceRegistry"));
+ }
for (PrototypeProperty child : typeContext.propertyData().properties()) {
if (isBuilder && child.configuredOption().hasAllowedValues()) {
String allowedValues = child.configuredOption().allowedValues()
@@ -527,62 +569,46 @@ private static void preBuildPrototypeMethod(InnerClass.Builder classBuilder,
.ifPresent(it -> preBuildBuilder.addContentLine("super.preBuildPrototype();"));
if (typeContext.propertyData().hasProvider()) {
boolean configured = typeContext.configuredData().configured();
+
if (configured) {
// need to have a non-null config instance
- preBuildBuilder.addContentLine("var config = this.config == null ? Config.empty() : this.config;");
+ preBuildBuilder.addContent("var config = this.config == null ? ")
+ .addContent(Types.COMMON_CONFIG)
+ .addContentLine(".empty() : this.config;");
}
+
+ if (typeContext.typeInfo().supportsServiceRegistry()) {
+ preBuildBuilder.addContent("var registry = this.serviceRegistry == null ? ")
+ .addContent(Types.GLOBAL_SERVICE_REGISTRY)
+ .addContentLine(".registry() : this.serviceRegistry;");
+ }
+
for (PrototypeProperty property : typeContext.propertyData().properties()) {
+ boolean propertyConfigured = property.configuredOption().configured();
+
AnnotationDataOption configuredOption = property.configuredOption();
if (configuredOption.provider()) {
boolean defaultDiscoverServices = configuredOption.providerDiscoverServices();
// using a code block, so we can reuse the same variable names for multiple providers
- preBuildBuilder.addContentLine("{");
TypeName providerType = configuredOption.providerType();
- preBuildBuilder.addContent("var serviceLoader = ")
- .addContent(HelidonServiceLoader.class)
- .addContent(".create(")
- .addContent(ServiceLoader.class)
- .addContent(".load(")
- .addContent(providerType.genericTypeName())
- .addContentLine(".class));");
- if (configured) {
- TypeName typeName = property.typeHandler().declaredType();
- if (typeName.isList() || typeName.isSet()) {
- preBuildBuilder.addContent("this.add")
- .addContent(capitalize(property.name()))
- .addContent("(discoverServices(config, \"")
- .addContent(configuredOption.configKey())
- .addContent("\", serviceLoader, ")
- .addContent(providerType.genericTypeName())
- .addContent(".class, ")
- .addContent(property.typeHandler().actualType().genericTypeName())
- .addContent(".class, ")
- .addContent(property.name())
- .addContent("DiscoverServices, ")
- .addContent(property.name())
- .addContentLine("));");
- } else {
- preBuildBuilder.addContent("discoverService(config, \"")
- .addContent(configuredOption.configKey())
- .addContent("\", serviceLoader, ")
- .addContent(providerType)
- .addContent(".class, ")
- .addContent(property.typeHandler().actualType().genericTypeName())
- .addContent(".class, ")
- .addContent(property.name())
- .addContent("DiscoverServices, @java.util.Optional@.ofNullable(")
- .addContent(property.name())
- .addContent(")).ifPresent(this::")
- .addContent(property.setterName())
- .addContentLine(");");
- }
+
+ if (typeContext.typeInfo().supportsServiceRegistry()) {
+ serviceRegistryPropertyDiscovery(preBuildBuilder,
+ property,
+ propertyConfigured,
+ configuredOption,
+ providerType,
+ defaultDiscoverServices);
} else {
- if (defaultDiscoverServices) {
- preBuildBuilder.addContentLine("this." + property.name() + "(serviceLoader.asList());");
- }
+ serviceLoaderPropertyDiscovery(preBuildBuilder,
+ property,
+ propertyConfigured,
+ configuredOption,
+ providerType,
+ defaultDiscoverServices);
}
- preBuildBuilder.addContentLine("}");
+
}
}
}
@@ -594,6 +620,130 @@ private static void preBuildPrototypeMethod(InnerClass.Builder classBuilder,
classBuilder.addMethod(preBuildBuilder);
}
+ private static void serviceLoaderPropertyDiscovery(Method.Builder preBuildBuilder,
+ PrototypeProperty property,
+ boolean propertyConfigured,
+ AnnotationDataOption configuredOption,
+ TypeName providerType,
+ boolean defaultDiscoverServices) {
+ preBuildBuilder.addContentLine("{");
+ preBuildBuilder.addContent("var serviceLoader = ")
+ .addContent(HelidonServiceLoader.class)
+ .addContent(".create(")
+ .addContent(ServiceLoader.class)
+ .addContent(".load(")
+ .addContent(providerType.genericTypeName())
+ .addContentLine(".class));");
+ if (propertyConfigured) {
+ TypeName typeName = property.typeHandler().declaredType();
+ if (typeName.isList() || typeName.isSet()) {
+ preBuildBuilder.addContent("this.add")
+ .addContent(capitalize(property.name()))
+ .addContent("(discoverServices(config, \"")
+ .addContent(configuredOption.configKey())
+ .addContent("\", serviceLoader, ")
+ .addContent(providerType.genericTypeName())
+ .addContent(".class, ")
+ .addContent(property.typeHandler().actualType().genericTypeName())
+ .addContent(".class, ")
+ .addContent(property.name())
+ .addContent("DiscoverServices, ")
+ .addContent(property.name())
+ .addContentLine("));");
+ } else {
+ preBuildBuilder.addContent("discoverService(config, \"")
+ .addContent(configuredOption.configKey())
+ .addContent("\", serviceLoader, ")
+ .addContent(providerType)
+ .addContent(".class, ")
+ .addContent(property.typeHandler().actualType().genericTypeName())
+ .addContent(".class, ")
+ .addContent(property.name())
+ .addContent("DiscoverServices, ")
+ .addContent(Optional.class)
+ .addContent(".ofNullable(")
+ .addContent(property.name())
+ .addContent(")).ifPresent(this::")
+ .addContent(property.setterName())
+ .addContentLine(");");
+ }
+ } else {
+ if (defaultDiscoverServices) {
+ preBuildBuilder.addContentLine("this." + property.name() + "(serviceLoader.asList());");
+ }
+ }
+ preBuildBuilder.addContentLine("}");
+ }
+
+ private static void serviceRegistryPropertyDiscovery(Method.Builder preBuildBuilder,
+ PrototypeProperty property,
+ boolean propertyConfigured,
+ AnnotationDataOption configuredOption,
+ TypeName providerType,
+ boolean defaultDiscoverServices) {
+ if (propertyConfigured) {
+ TypeName typeName = property.typeHandler().declaredType();
+ if (typeName.isList() || typeName.isSet()) {
+ preBuildBuilder.addContent("this.add")
+ .addContent(capitalize(property.name()))
+ .addContent("(")
+ .addContent(Types.GENERATED_SERVICE)
+ .addContentLine(".discoverServices(config,")
+ .increaseContentPadding()
+ .increaseContentPadding()
+ .increaseContentPadding()
+ .addContent("\"")
+ .addContent(configuredOption.configKey())
+ .addContentLine("\",")
+ .addContentLine("registry,")
+ .addContent(providerType.genericTypeName())
+ .addContentLine(".class,")
+ .addContent(property.typeHandler().actualType().genericTypeName())
+ .addContentLine(".class,")
+ .addContent(property.name())
+ .addContentLine("DiscoverServices,")
+ .addContent(property.name())
+ .addContentLine("));")
+ .decreaseContentPadding()
+ .decreaseContentPadding()
+ .decreaseContentPadding();
+ } else {
+ preBuildBuilder
+ .addContent(Types.GENERATED_SERVICE)
+ .addContentLine(".discoverService(config,")
+ .increaseContentPadding()
+ .increaseContentPadding()
+ .increaseContentPadding()
+ .addContent("\"")
+ .addContent(configuredOption.configKey())
+ .addContentLine("\",")
+ .addContentLine("registry,")
+ .addContent(providerType)
+ .addContentLine(".class,")
+ .addContent(property.typeHandler().actualType().genericTypeName())
+ .addContentLine(".class,")
+ .addContent(property.name())
+ .addContentLine("DiscoverServices,")
+ .addContent(Optional.class)
+ .addContent(".ofNullable(")
+ .addContent(property.name())
+ .addContentLine("))")
+ .decreaseContentPadding()
+ .decreaseContentPadding()
+ .addContent(".ifPresent(this::")
+ .addContent(property.setterName())
+ .addContentLine(");")
+ .decreaseContentPadding();
+ }
+ } else {
+ if (defaultDiscoverServices) {
+ preBuildBuilder.addContent("this." + property.name() + "(registry.all(")
+ .addContent(providerType.genericTypeName())
+ .addContentLine(".class));");
+ }
+ }
+ }
+
private static void validatePrototypeMethod(InnerClass.Builder classBuilder, TypeContext typeContext) {
Method.Builder validateBuilder = Method.builder()
.name("validatePrototype")
diff --git a/builder/codegen/src/main/java/io/helidon/builder/codegen/TypeContext.java b/builder/codegen/src/main/java/io/helidon/builder/codegen/TypeContext.java
index b613f44f466..12b74b32555 100644
--- a/builder/codegen/src/main/java/io/helidon/builder/codegen/TypeContext.java
+++ b/builder/codegen/src/main/java/io/helidon/builder/codegen/TypeContext.java
@@ -203,7 +203,12 @@ static TypeContext create(CodegenContext ctx, TypeInfo blueprint) {
.className("Builder")
.build();
- TypeInformation typeInformation = new TypeInformation(blueprint,
+ boolean supportsServiceRegistry = blueprint.findAnnotation(Types.PROTOTYPE_SERVICE_REGISTRY)
+ .flatMap(Annotation::booleanValue)
+ .orElse(false);
+
+ TypeInformation typeInformation = new TypeInformation(supportsServiceRegistry,
+ blueprint,
prototype,
prototypeBuilder,
prototypeImpl,
@@ -407,6 +412,7 @@ private static TypeName generatedTypeName(TypeInfo typeInfo) {
}
record TypeInformation(
+ boolean supportsServiceRegistry,
TypeInfo blueprintType,
TypeName prototype,
TypeName prototypeBuilder,
diff --git a/builder/codegen/src/main/java/io/helidon/builder/codegen/Types.java b/builder/codegen/src/main/java/io/helidon/builder/codegen/Types.java
index ea6b79a1c85..8a274abb2cb 100644
--- a/builder/codegen/src/main/java/io/helidon/builder/codegen/Types.java
+++ b/builder/codegen/src/main/java/io/helidon/builder/codegen/Types.java
@@ -31,6 +31,9 @@ final class Types {
static final TypeName ARRAY_LIST = TypeName.create(ArrayList.class);
static final TypeName LINKED_HASH_SET = TypeName.create(LinkedHashSet.class);
static final TypeName CHAR_ARRAY = TypeName.create(char[].class);
+ static final TypeName SERVICE_REGISTRY = TypeName.create("io.helidon.service.registry.ServiceRegistry");
+ static final TypeName GLOBAL_SERVICE_REGISTRY = TypeName.create("io.helidon.service.registry.GlobalServiceRegistry");
+ static final TypeName GENERATED_SERVICE = TypeName.create("io.helidon.service.registry.GeneratedService");
static final TypeName BUILDER_DESCRIPTION = TypeName.create("io.helidon.builder.api.Description");
@@ -48,6 +51,7 @@ final class Types {
static final TypeName PROTOTYPE_PROTOTYPE_METHOD = TypeName.create("io.helidon.builder.api.Prototype.PrototypeMethod");
static final TypeName PROTOTYPE_BUILDER_DECORATOR = TypeName.create("io.helidon.builder.api.Prototype.BuilderDecorator");
static final TypeName PROTOTYPE_CONSTANT = TypeName.create("io.helidon.builder.api.Prototype.Constant");
+ static final TypeName PROTOTYPE_SERVICE_REGISTRY = TypeName.create("io.helidon.builder.api.Prototype.RegistrySupport");
static final TypeName GENERATED_EQUALITY_UTIL = TypeName.create("io.helidon.builder.api.GeneratedBuilder.EqualityUtil");
static final TypeName RUNTIME_PROTOTYPE = TypeName.create("io.helidon.builder.api.RuntimeType.PrototypedBy");
diff --git a/builder/tests/builder/pom.xml b/builder/tests/builder/pom.xml
index 43e8440a93e..0a1c918a941 100644
--- a/builder/tests/builder/pom.xml
+++ b/builder/tests/builder/pom.xml
@@ -36,6 +36,10 @@
io.helidon.builderhelidon-builder-api
+
+ io.helidon.service
+ helidon-service-registry
+ com.fasterxml.jackson.corejackson-databind
diff --git a/builder/tests/builder/src/main/java/io/helidon/builder/test/testsubjects/WithProviderRegistryBlueprint.java b/builder/tests/builder/src/main/java/io/helidon/builder/test/testsubjects/WithProviderRegistryBlueprint.java
new file mode 100644
index 00000000000..37d6a35c534
--- /dev/null
+++ b/builder/tests/builder/src/main/java/io/helidon/builder/test/testsubjects/WithProviderRegistryBlueprint.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2024 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.builder.test.testsubjects;
+
+import java.util.List;
+import java.util.Optional;
+
+import io.helidon.builder.api.Option;
+import io.helidon.builder.api.Prototype;
+
+@Prototype.Blueprint
+@Prototype.Configured
+@Prototype.RegistrySupport
+interface WithProviderRegistryBlueprint {
+ @Option.Configured
+ @Option.Provider(SomeProvider.class)
+ SomeProvider.SomeService oneDiscover();
+
+ @Option.Configured
+ @Option.Provider(value = SomeProvider.class, discoverServices = false)
+ SomeProvider.SomeService oneNotDiscover();
+
+ @Option.Configured
+ @Option.Provider(SomeProvider.class)
+ Optional optionalDiscover();
+
+ @Option.Configured
+ @Option.Provider(value = SomeProvider.class, discoverServices = false)
+ Optional optionalNotDiscover();
+
+ @Option.Configured
+ @Option.Provider(SomeProvider.class)
+ List listDiscover();
+
+ @Option.Configured
+ @Option.Provider(value = SomeProvider.class, discoverServices = false)
+ List listNotDiscover();
+
+ /*
+ The following should always be empty, as there are no implementations
+ */
+ @Option.Configured
+ @Option.Provider(ProviderNoImpls.class)
+ Optional optionalNoImplDiscover();
+
+ @Option.Configured
+ @Option.Provider(value = ProviderNoImpls.class, discoverServices = false)
+ Optional optionalNoImplNotDiscover();
+
+ @Option.Configured
+ @Option.Provider(ProviderNoImpls.class)
+ List listNoImplDiscover();
+
+ @Option.Configured
+ @Option.Provider(value = ProviderNoImpls.class, discoverServices = false)
+ List listNoImplNotDiscover();
+}
diff --git a/builder/tests/builder/src/main/java/module-info.java b/builder/tests/builder/src/main/java/module-info.java
index 1d04dd1056f..f1db16c08c2 100644
--- a/builder/tests/builder/src/main/java/module-info.java
+++ b/builder/tests/builder/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 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.
@@ -24,6 +24,7 @@
requires io.helidon.common;
requires io.helidon.common.config;
requires io.helidon.builder.api;
+ requires io.helidon.service.registry;
exports io.helidon.builder.test.testsubjects;
diff --git a/builder/tests/builder/src/test/java/io/helidon/builder/test/ProviderRegistryTest.java b/builder/tests/builder/src/test/java/io/helidon/builder/test/ProviderRegistryTest.java
new file mode 100644
index 00000000000..186bb4da369
--- /dev/null
+++ b/builder/tests/builder/src/test/java/io/helidon/builder/test/ProviderRegistryTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2024 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.builder.test;
+
+import java.util.List;
+
+import io.helidon.builder.test.testsubjects.SomeProvider;
+import io.helidon.builder.test.testsubjects.WithProviderRegistry;
+import io.helidon.common.Errors;
+import io.helidon.config.Config;
+import io.helidon.config.ConfigSources;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static io.helidon.common.testing.junit5.OptionalMatcher.optionalEmpty;
+import static io.helidon.common.testing.junit5.OptionalMatcher.optionalValue;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ProviderRegistryTest {
+ private static Config config;
+
+ @BeforeAll
+ static void beforeAll() {
+ config = Config.just(ConfigSources.classpath("provider-test.yaml"));
+ }
+
+ @Test
+ void testMinimalDefined() {
+ /*
+ Only the property that does not discover services and does not return optional is defined in config
+ */
+ WithProviderRegistry value = WithProviderRegistry.create(config.get("min-defined"));
+
+ assertThat(value.oneDiscover().prop(), is("some-1"));
+ assertThat(value.oneNotDiscover().prop(), is("config"));
+
+ assertThat(value.optionalDiscover().map(SomeProvider.SomeService::prop), optionalValue(is("some-1")));
+ assertThat(value.optionalNotDiscover().map(SomeProvider.SomeService::prop), optionalEmpty());
+
+ assertThat(value.listDiscover(), hasSize(2));
+ assertThat(value.listNotDiscover(), hasSize(0));
+
+ /*
+ Test all the methods for a provider with no implementations
+ */
+ assertThat(value.optionalNoImplDiscover(), optionalEmpty());
+ assertThat(value.optionalNoImplNotDiscover(), optionalEmpty());
+ assertThat(value.listNoImplDiscover(), hasSize(0));
+ assertThat(value.listNoImplNotDiscover(), hasSize(0));
+ }
+
+ @Test
+ void testAllDefined() {
+ /*
+ Everything is customized
+ */
+ WithProviderRegistry value = WithProviderRegistry.create(config.get("all-defined"));
+
+ assertThat(value.oneDiscover().prop(), is("config"));
+ assertThat(value.oneNotDiscover().prop(), is("config"));
+
+ assertThat(value.optionalDiscover().map(SomeProvider.SomeService::prop), optionalValue(is("config")));
+ assertThat(value.optionalNotDiscover().map(SomeProvider.SomeService::prop), optionalValue(is("config")));
+
+ List services = value.listDiscover();
+ assertThat(services, hasSize(2));
+ assertThat(services.get(0).prop(), is("config"));
+ assertThat(services.get(1).prop(), is("config2"));
+
+ services = value.listNotDiscover();
+ assertThat(value.listNotDiscover(), hasSize(2));
+ assertThat(services.get(0).prop(), is("config"));
+ assertThat(services.get(1).prop(), is("config2"));
+ }
+
+ @Test
+ void testFail() {
+ /*
+ Missing the one mandatory option in config
+ */
+ Errors.ErrorMessagesException fail = assertThrows(Errors.ErrorMessagesException.class,
+ () -> WithProviderRegistry.create(config.get("fail")));
+
+ assertThat(fail.getMessages(), hasSize(1));
+ assertThat(fail.getMessage(), containsString("\"one-not-discover\" must not be null"));
+ }
+
+ @Test
+ void testSingleList() {
+ /*
+ Single value list (when not using discovery). When discovery is used, all in service loader are discovered, even
+ if not configured
+ */
+ WithProviderRegistry value = WithProviderRegistry.create(config.get("single-list"));
+
+ assertThat(value.oneNotDiscover().prop(), is("config2"));
+
+ List services = value.listDiscover();
+ assertThat(services, hasSize(2));
+ assertThat(services.get(0).prop(), is("config"));
+ assertThat(services.get(1).prop(), is("some-2"));
+
+ services = value.listNotDiscover();
+ assertThat(value.listNotDiscover(), hasSize(1));
+ assertThat(services.get(0).prop(), is("config2"));
+ }
+
+ @Test
+ void testDisabledDiscoveryOnTheCopy() {
+ SomeProvider.SomeService someService = new DummyService();
+ WithProviderRegistry value = WithProviderRegistry.builder()
+ .listDiscoverDiscoverServices(false)
+ .oneNotDiscover(someService) //This needs to be set, otherwise validation fails
+ .build();
+ assertThat(value.listDiscover(), is(List.of()));
+
+ WithProviderRegistry copy = WithProviderRegistry.builder()
+ .from(value)
+ .build();
+ assertThat(copy.listDiscover(), is(List.of()));
+ }
+
+ @Test
+ void testDisabledDiscoveryOnTheCopiedBuilder() {
+ SomeProvider.SomeService someService = new DummyService();
+ WithProviderRegistry.Builder value = WithProviderRegistry.builder()
+ .listDiscoverDiscoverServices(false)
+ .oneNotDiscover(someService);
+
+ WithProviderRegistry copy = WithProviderRegistry.builder()
+ .from(value)
+ .build();
+ assertThat(copy.listDiscover(), is(List.of()));
+ }
+
+ private static class DummyService implements SomeProvider.SomeService {
+ @Override
+ public String prop() {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return null;
+ }
+
+ @Override
+ public String type() {
+ return null;
+ }
+ }
+
+}
diff --git a/builder/tests/codegen/pom.xml b/builder/tests/codegen/pom.xml
new file mode 100644
index 00000000000..6d0ae10cf9d
--- /dev/null
+++ b/builder/tests/codegen/pom.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+ io.helidon.builder.tests
+ helidon-builder-tests-project
+ 4.0.0-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ helidon-builder-tests-codegen
+ Helidon Builder Tests Codegen
+
+
+
+
+ io.helidon.builder
+ helidon-builder-codegen
+ test
+
+
+
+ io.helidon.common
+ helidon-common-config
+ test
+
+
+ io.helidon.service
+ helidon-service-registry
+ test
+
+
+ io.helidon.builder
+ helidon-builder-api
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
diff --git a/builder/tests/codegen/src/test/java/io/helidon/builder/codegen/TypesTest.java b/builder/tests/codegen/src/test/java/io/helidon/builder/codegen/TypesTest.java
new file mode 100644
index 00000000000..f5c1d35db9c
--- /dev/null
+++ b/builder/tests/codegen/src/test/java/io/helidon/builder/codegen/TypesTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2024 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.builder.codegen;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import io.helidon.builder.api.Description;
+import io.helidon.builder.api.GeneratedBuilder;
+import io.helidon.builder.api.Option;
+import io.helidon.builder.api.Prototype;
+import io.helidon.builder.api.RuntimeType;
+import io.helidon.common.Generated;
+import io.helidon.common.config.Config;
+import io.helidon.common.types.TypeName;
+import io.helidon.service.registry.GeneratedService;
+import io.helidon.service.registry.GlobalServiceRegistry;
+import io.helidon.service.registry.ServiceRegistry;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.collection.IsEmptyCollection;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class TypesTest {
+ @Test
+ void testTypes() {
+ // it is really important to test ALL constants on the class, so let's use reflection
+ Field[] declaredFields = Types.class.getDeclaredFields();
+
+ Set toCheck = new HashSet<>();
+ Set checked = new HashSet<>();
+ Map fields = new HashMap<>();
+
+ for (Field declaredField : declaredFields) {
+ String name = declaredField.getName();
+
+ assertThat(name + " must be a TypeName", declaredField.getType(), CoreMatchers.sameInstance(TypeName.class));
+ assertThat(name + " must be static", Modifier.isStatic(declaredField.getModifiers()), is(true));
+ assertThat(name + " must be package local, not public",
+ Modifier.isPublic(declaredField.getModifiers()),
+ is(false));
+ assertThat(name + " must be package local, not private",
+ Modifier.isPrivate(declaredField.getModifiers()),
+ is(false));
+ assertThat(name + " must be package local, not protected",
+ Modifier.isProtected(declaredField.getModifiers()),
+ is(false));
+ assertThat(name + " must be final", Modifier.isFinal(declaredField.getModifiers()), is(true));
+
+ toCheck.add(name);
+ fields.put(name, declaredField);
+ }
+
+ checkField(toCheck, checked, fields, "COMMON_CONFIG", Config.class);
+ checkField(toCheck, checked, fields, "GENERATED", Generated.class);
+ checkField(toCheck, checked, fields, "DEPRECATED", Deprecated.class);
+ checkField(toCheck, checked, fields, "LINKED_HASH_MAP", LinkedHashMap.class);
+ checkField(toCheck, checked, fields, "ARRAY_LIST", ArrayList.class);
+ checkField(toCheck, checked, fields, "LINKED_HASH_SET", LinkedHashSet.class);
+ checkField(toCheck, checked, fields, "CHAR_ARRAY", char[].class);
+ checkField(toCheck, checked, fields, "SERVICE_REGISTRY", ServiceRegistry.class);
+ checkField(toCheck, checked, fields, "GLOBAL_SERVICE_REGISTRY", GlobalServiceRegistry.class);
+ checkField(toCheck, checked, fields, "GENERATED_SERVICE", GeneratedService.class);
+ checkField(toCheck, checked, fields, "BUILDER_DESCRIPTION", Description.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_BLUEPRINT", Prototype.Blueprint.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_IMPLEMENT", Prototype.Implement.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_API", Prototype.Api.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_ANNOTATED", Prototype.Annotated.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_FACTORY", Prototype.Factory.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_CONFIGURED", Prototype.Configured.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_BUILDER", Prototype.Builder.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_CONFIGURED_BUILDER", Prototype.ConfiguredBuilder.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_CUSTOM_METHODS", Prototype.CustomMethods.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_FACTORY_METHOD", Prototype.FactoryMethod.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_BUILDER_METHOD", Prototype.BuilderMethod.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_PROTOTYPE_METHOD", Prototype.PrototypeMethod.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_BUILDER_DECORATOR", Prototype.BuilderDecorator.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_CONSTANT", Prototype.Constant.class);
+ checkField(toCheck, checked, fields, "PROTOTYPE_SERVICE_REGISTRY", Prototype.RegistrySupport.class);
+ checkField(toCheck, checked, fields, "GENERATED_EQUALITY_UTIL", GeneratedBuilder.EqualityUtil.class);
+ checkField(toCheck, checked, fields, "RUNTIME_PROTOTYPE", RuntimeType.PrototypedBy.class);
+ checkField(toCheck, checked, fields, "RUNTIME_PROTOTYPED_BY", RuntimeType.PrototypedBy.class);
+ checkField(toCheck, checked, fields, "RUNTIME_API", RuntimeType.Api.class);
+ checkField(toCheck, checked, fields, "OPTION_SAME_GENERIC", Option.SameGeneric.class);
+ checkField(toCheck, checked, fields, "OPTION_SINGULAR", Option.Singular.class);
+ checkField(toCheck, checked, fields, "OPTION_CONFIDENTIAL", Option.Confidential.class);
+ checkField(toCheck, checked, fields, "OPTION_REDUNDANT", Option.Redundant.class);
+ checkField(toCheck, checked, fields, "OPTION_CONFIGURED", Option.Configured.class);
+ checkField(toCheck, checked, fields, "OPTION_ACCESS", Option.Access.class);
+ checkField(toCheck, checked, fields, "OPTION_REQUIRED", Option.Required.class);
+ checkField(toCheck, checked, fields, "OPTION_PROVIDER", Option.Provider.class);
+ checkField(toCheck, checked, fields, "OPTION_ALLOWED_VALUES", Option.AllowedValues.class);
+ checkField(toCheck, checked, fields, "OPTION_ALLOWED_VALUE", Option.AllowedValue.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT", Option.Default.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT_INT", Option.DefaultInt.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT_DOUBLE", Option.DefaultDouble.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT_BOOLEAN", Option.DefaultBoolean.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT_LONG", Option.DefaultLong.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT_METHOD", Option.DefaultMethod.class);
+ checkField(toCheck, checked, fields, "OPTION_DEFAULT_CODE", Option.DefaultCode.class);
+ checkField(toCheck, checked, fields, "OPTION_DEPRECATED", Option.Deprecated.class);
+ checkField(toCheck, checked, fields, "OPTION_TYPE", Option.Type.class);
+ checkField(toCheck, checked, fields, "OPTION_DECORATOR", Option.Decorator.class);
+
+ assertThat(toCheck, IsEmptyCollection.empty());
+ }
+
+ private void checkField(Set namesToCheck,
+ Set checkedNames,
+ Map namesToFields,
+ String name,
+ Class> expectedType) {
+ Field field = namesToFields.get(name);
+ assertThat("Field " + name + " does not exist in the class", field, notNullValue());
+ try {
+ namesToCheck.remove(name);
+ if (checkedNames.add(name)) {
+ TypeName value = (TypeName) field.get(null);
+ assertThat("Field " + name, value.fqName(), is(expectedType.getCanonicalName()));
+ } else {
+ fail("Field " + name + " is checked more than once.class");
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/Annotated.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/Annotated.java
index 3be2ab6f8ae..07f13ac5c29 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/Annotated.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/Annotated.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -16,6 +16,7 @@
package io.helidon.common.types;
+import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
@@ -27,14 +28,38 @@
*/
public interface Annotated {
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @return the list of annotations on this element
+ * @return the list of annotations declared on this element
*/
@Option.Singular
List annotations();
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @return list of all meta annotations of this element
+ */
+ @Option.Singular
+ List inheritedAnnotations();
+
+ /**
+ * List of all annotations - both {@link #annotations()} and {@link #inheritedAnnotations()}.
+ *
+ * @return list of all annotations valid for this element
+ */
+ default List allAnnotations() {
+ List result = new ArrayList<>(annotations());
+ result.addAll(inheritedAnnotations());
+ return List.copyOf(result);
+ }
+
/**
* Find an annotation on this annotated type.
*
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/AnnotationBlueprint.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/AnnotationBlueprint.java
index 26e86945a7b..3017fdd6526 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/AnnotationBlueprint.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/AnnotationBlueprint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -58,6 +58,9 @@
@Prototype.Implement("java.lang.Comparable")
interface AnnotationBlueprint {
+ /**
+ * The "{@code value}" property name.
+ */
String VALUE_PROPERTY = "value";
/**
@@ -88,11 +91,11 @@ default Optional value() {
/**
* Get a value of an annotation property.
*
- * @param name name of the annotation property
+ * @param property name of the annotation property
* @return string value of the property
*/
- default Optional getValue(String name) {
- return AnnotationSupport.asString(typeName(), values(), name);
+ default Optional getValue(String property) {
+ return AnnotationSupport.asString(typeName(), values(), property);
}
/**
@@ -111,6 +114,7 @@ default Optional objectValue() {
* The type can be either String, or any primitive type, or {@link io.helidon.common.types.Annotation},
* or list of these.
*
+ * @param property name of the annotation property
* @return object value
*/
default Optional objectValue(String property) {
@@ -129,6 +133,7 @@ default Optional stringValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional stringValue(String property) {
@@ -149,6 +154,7 @@ default Optional> stringValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> stringValues(String property) {
@@ -167,6 +173,7 @@ default Optional intValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional intValue(String property) {
@@ -187,6 +194,7 @@ default Optional> intValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> intValues(String property) {
@@ -205,6 +213,7 @@ default Optional longValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional longValue(String property) {
@@ -225,6 +234,7 @@ default Optional> longValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> longValues(String property) {
@@ -243,6 +253,7 @@ default Optional booleanValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional booleanValue(String property) {
@@ -263,6 +274,7 @@ default Optional> booleanValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> booleanValues(String property) {
@@ -281,6 +293,7 @@ default Optional byteValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional byteValue(String property) {
@@ -301,6 +314,7 @@ default Optional> byteValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> byteValues(String property) {
@@ -319,6 +333,7 @@ default Optional charValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional charValue(String property) {
@@ -339,6 +354,7 @@ default Optional> charValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> charValues(String property) {
@@ -357,6 +373,7 @@ default Optional shortValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional shortValue(String property) {
@@ -377,6 +394,7 @@ default Optional> shortValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> shortValues(String property) {
@@ -395,6 +413,7 @@ default Optional floatValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional floatValue(String property) {
@@ -415,6 +434,7 @@ default Optional> floatValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> floatValues(String property) {
@@ -433,6 +453,7 @@ default Optional doubleValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional doubleValue(String property) {
@@ -453,6 +474,7 @@ default Optional> doubleValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> doubleValues(String property) {
@@ -471,6 +493,7 @@ default Optional> classValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional> classValue(String property) {
@@ -491,6 +514,7 @@ default Optional>> classValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional>> classValues(String property) {
@@ -511,6 +535,7 @@ default Optional typeValue() {
* Typed value of a named property.
* Alternative for {@link #classValue(String)}.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional typeValue(String property) {
@@ -533,6 +558,7 @@ default Optional> typeValues() {
* This will also work for a single values property.
* Alternative for {@link #classValues(String)}.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> typeValues(String property) {
@@ -551,6 +577,7 @@ default Optional annotationValue() {
/**
* Typed value of a named property.
*
+ * @param property name of the annotation property
* @return value if present
*/
default Optional annotationValue(String property) {
@@ -571,6 +598,7 @@ default Optional> annotationValues() {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
+ * @param property name of the annotation property
* @return list of defined values if present
*/
default Optional> annotationValues(String property) {
@@ -591,8 +619,9 @@ default > Optional enumValue(Class type) {
/**
* Typed value of a named property.
*
- * @param type class of the enumeration
- * @param type of the enumeration
+ * @param property name of the annotation property
+ * @param type class of the enumeration
+ * @param type of the enumeration
* @return value if present
*/
default > Optional enumValue(String property, Class type) {
@@ -615,8 +644,9 @@ default > Optional> enumValues(Class type) {
* Typed values of a property that is defined as an array.
* This will also work for a single values property.
*
- * @param type class of the enumeration
- * @param type of the enumeration
+ * @param property name of the annotation property
+ * @param type class of the enumeration
+ * @param type of the enumeration
* @return list of defined values if present
*/
default > Optional> enumValues(String property, Class type) {
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeInfoBlueprint.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeInfoBlueprint.java
index 8820e70fe50..4bb3da96300 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeInfoBlueprint.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeInfoBlueprint.java
@@ -58,7 +58,7 @@ interface TypeInfoBlueprint extends Annotated {
*
* @return the type element kind.
* @see io.helidon.common.types.TypeValues#KIND_CLASS and other constants on this class prefixed with {@code TYPE}
- * @deprecated use {@link #kind()} instead
+ * @deprecated use {@link io.helidon.common.types.TypeInfo#kind()} instead
*/
@Option.Required
@Option.Deprecated("kind")
@@ -186,7 +186,7 @@ default Optional metaAnnotation(TypeName annotation, TypeName metaAn
*
* @return element modifiers
* @see io.helidon.common.types.TypeValues#MODIFIER_PUBLIC and other constants prefixed with {@code MODIFIER}
- * @deprecated use {@link #elementModifiers()} instead
+ * @deprecated use {@link io.helidon.common.types.TypeInfo#elementModifiers()} instead
*/
@Option.Singular
@Option.Redundant
@@ -229,7 +229,8 @@ default Optional metaAnnotation(TypeName annotation, TypeName metaAn
Optional originatingElement();
/**
- * Uses {@link #referencedModuleNames()} to determine if the module name is known for the given type.
+ * Uses {@link io.helidon.common.types.TypeInfo#referencedModuleNames()} to determine if the module name is known for the
+ * given type.
*
* @param typeName the type name to lookup
* @return the module name if it is known
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameBlueprint.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameBlueprint.java
index 0db490e65c4..1d9d5fa7118 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameBlueprint.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameBlueprint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -178,6 +178,15 @@ default boolean isOptional() {
return TypeNames.OPTIONAL.name().equals(name());
}
+ /**
+ * Indicates whether this type is a {@link java.util.function.Supplier}.
+ *
+ * @return if this is a supplier
+ */
+ default boolean isSupplier() {
+ return TypeNames.SUPPLIER.fqName().equals(fqName());
+ }
+
/**
* Simple class name with generic declaration (if part of this name).
*
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameSupport.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameSupport.java
index 72ee4546018..e8a9d38809b 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameSupport.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNameSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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,14 +20,58 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Stream;
import io.helidon.builder.api.Prototype;
-import static io.helidon.common.types.TypeNames.PRIMITIVES;
-
final class TypeNameSupport {
+ private static final TypeName PRIMITIVE_BOOLEAN = TypeName.create(boolean.class);
+ private static final TypeName PRIMITIVE_BYTE = TypeName.create(byte.class);
+ private static final TypeName PRIMITIVE_SHORT = TypeName.create(short.class);
+ private static final TypeName PRIMITIVE_INT = TypeName.create(int.class);
+ private static final TypeName PRIMITIVE_LONG = TypeName.create(long.class);
+ private static final TypeName PRIMITIVE_CHAR = TypeName.create(char.class);
+ private static final TypeName PRIMITIVE_FLOAT = TypeName.create(float.class);
+ private static final TypeName PRIMITIVE_DOUBLE = TypeName.create(double.class);
+ private static final TypeName PRIMITIVE_VOID = TypeName.create(void.class);
+ private static final TypeName BOXED_BOOLEAN = TypeName.create(Boolean.class);
+ private static final TypeName BOXED_BYTE = TypeName.create(Byte.class);
+ private static final TypeName BOXED_SHORT = TypeName.create(Short.class);
+ private static final TypeName BOXED_INT = TypeName.create(Integer.class);
+ private static final TypeName BOXED_LONG = TypeName.create(Long.class);
+ private static final TypeName BOXED_CHAR = TypeName.create(Character.class);
+ private static final TypeName BOXED_FLOAT = TypeName.create(Float.class);
+ private static final TypeName BOXED_DOUBLE = TypeName.create(Double.class);
+ private static final TypeName BOXED_VOID = TypeName.create(Void.class);
+
+ // as type names need this class to be initialized, let's have a copy of these
+ private static final Map PRIMITIVES = Map.of(
+ "boolean", PRIMITIVE_BOOLEAN,
+ "byte", PRIMITIVE_BYTE,
+ "short", PRIMITIVE_SHORT,
+ "int", PRIMITIVE_INT,
+ "long", PRIMITIVE_LONG,
+ "char", PRIMITIVE_CHAR,
+ "float", PRIMITIVE_FLOAT,
+ "double", PRIMITIVE_DOUBLE,
+ "void", PRIMITIVE_VOID
+ );
+
+ private static final Map BOXED_TYPES = Map.of(
+ PRIMITIVE_BOOLEAN, BOXED_BOOLEAN,
+ PRIMITIVE_BYTE, BOXED_BYTE,
+ PRIMITIVE_SHORT, BOXED_SHORT,
+ PRIMITIVE_INT, BOXED_INT,
+ PRIMITIVE_LONG, BOXED_LONG,
+ PRIMITIVE_CHAR, BOXED_CHAR,
+ PRIMITIVE_FLOAT, BOXED_FLOAT,
+ PRIMITIVE_DOUBLE, BOXED_DOUBLE,
+ PRIMITIVE_VOID, BOXED_VOID
+ );
+
private TypeNameSupport() {
}
@@ -55,7 +99,8 @@ static int compareTo(TypeName typeName, TypeName o) {
*/
@Prototype.PrototypeMethod
static TypeName boxed(TypeName original) {
- return TypeNames.boxed(original);
+ return Optional.ofNullable(BOXED_TYPES.get(original))
+ .orElse(original);
}
@Prototype.PrototypeMethod
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNames.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNames.java
index 20e860c9093..62fe59da5a3 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNames.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypeNames.java
@@ -16,6 +16,8 @@
package io.helidon.common.types;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
@@ -24,6 +26,9 @@
import java.util.Set;
import java.util.function.Supplier;
+import io.helidon.common.Generated;
+import io.helidon.common.GenericType;
+
/**
* Commonly used type names.
*/
@@ -64,6 +69,15 @@ public final class TypeNames {
* Type name for {@link java.time.Duration}.
*/
public static final TypeName DURATION = TypeName.create(Duration.class);
+ /**
+ * Type name for {@link java.lang.annotation.Retention}.
+ */
+ public static final TypeName RETENTION = TypeName.create(Retention.class);
+ /**
+ * Type name for {@link java.lang.annotation.Inherited}.
+ */
+ public static final TypeName INHERITED = TypeName.create(Inherited.class);
+
/*
Primitive types and their boxed counterparts
*/
@@ -151,36 +165,27 @@ public final class TypeNames {
* Type name of typed element info.
*/
public static final TypeName TYPED_ELEMENT_INFO = TypeName.create(TypedElementInfo.class);
-
- static final Map PRIMITIVES = Map.of(
- "boolean", PRIMITIVE_BOOLEAN,
- "byte", PRIMITIVE_BYTE,
- "short", PRIMITIVE_SHORT,
- "int", PRIMITIVE_INT,
- "long", PRIMITIVE_LONG,
- "char", PRIMITIVE_CHAR,
- "float", PRIMITIVE_FLOAT,
- "double", PRIMITIVE_DOUBLE,
- "void", PRIMITIVE_VOID
- );
-
- private static final Map BOXED_TYPES = Map.of(
- PRIMITIVE_BOOLEAN, BOXED_BOOLEAN,
- PRIMITIVE_BYTE, BOXED_BYTE,
- PRIMITIVE_SHORT, BOXED_SHORT,
- PRIMITIVE_INT, BOXED_INT,
- PRIMITIVE_LONG, BOXED_LONG,
- PRIMITIVE_CHAR, BOXED_CHAR,
- PRIMITIVE_FLOAT, BOXED_FLOAT,
- PRIMITIVE_DOUBLE, BOXED_DOUBLE,
- PRIMITIVE_VOID, BOXED_VOID
- );
+ /**
+ * Helidon annotation type.
+ */
+ public static final TypeName ANNOTATION = TypeName.create(Annotation.class);
+ /**
+ * Helidon element kind (enum).
+ */
+ public static final TypeName ELEMENT_KIND = TypeName.create(ElementKind.class);
+ /**
+ * Helidon access modifier (enum).
+ */
+ public static final TypeName ACCESS_MODIFIER = TypeName.create(AccessModifier.class);
+ /**
+ * Helidon Generated annotation type.
+ */
+ public static final TypeName GENERATED = TypeName.create(Generated.class);
+ /**
+ * Helidon {@link io.helidon.common.GenericType}.
+ */
+ public static final TypeName GENERIC_TYPE = TypeName.create(GenericType.class);
private TypeNames() {
}
-
- static TypeName boxed(TypeName original) {
- return Optional.ofNullable(BOXED_TYPES.get(original))
- .orElse(original);
- }
}
diff --git a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypedElementInfoBlueprint.java b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypedElementInfoBlueprint.java
index e5a57c7f048..b52ba88a4bd 100644
--- a/builder/tests/common-types/src/main/java/io/helidon/common/types/TypedElementInfoBlueprint.java
+++ b/builder/tests/common-types/src/main/java/io/helidon/common/types/TypedElementInfoBlueprint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -59,7 +59,7 @@ interface TypedElementInfoBlueprint extends Annotated {
*
* @return the element kind
* @see io.helidon.common.types.TypeInfo
- * @deprecated use {@link #kind()} instead
+ * @deprecated use {@link io.helidon.common.types.TypedElementInfo#kind()} instead
*/
@Option.Required
@Option.Deprecated("kind")
@@ -84,7 +84,7 @@ interface TypedElementInfoBlueprint extends Annotated {
Optional defaultValue();
/**
- * The list of known annotations on the type name referenced by {@link #typeName()}.
+ * The list of known annotations on the type name referenced by {@link io.helidon.common.types.TypedElementInfo#typeName()}.
*
* @return the list of annotations on this element's (return) type.
*/
@@ -104,7 +104,7 @@ interface TypedElementInfoBlueprint extends Annotated {
*
* @return element modifiers
* @see io.helidon.common.types.TypeInfo
- * @deprecated use {@link #elementModifiers()} instead
+ * @deprecated use {@link io.helidon.common.types.TypedElementInfo#elementModifiers()} instead
*/
@Option.Singular
@Option.Redundant
diff --git a/builder/tests/pom.xml b/builder/tests/pom.xml
index 68c85a67f13..c21c203f44c 100644
--- a/builder/tests/pom.xml
+++ b/builder/tests/pom.xml
@@ -47,6 +47,7 @@
common-typesbuilder
+ codegen
diff --git a/codegen/apt/src/main/java/io/helidon/codegen/apt/AptFiler.java b/codegen/apt/src/main/java/io/helidon/codegen/apt/AptFiler.java
index 801cd1baa66..84a7bbf2fab 100644
--- a/codegen/apt/src/main/java/io/helidon/codegen/apt/AptFiler.java
+++ b/codegen/apt/src/main/java/io/helidon/codegen/apt/AptFiler.java
@@ -16,7 +16,9 @@
package io.helidon.codegen.apt;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.file.Path;
@@ -33,9 +35,12 @@
import io.helidon.codegen.CodegenException;
import io.helidon.codegen.CodegenFiler;
import io.helidon.codegen.CodegenOptions;
+import io.helidon.codegen.FilerTextResource;
import io.helidon.codegen.IndentType;
import io.helidon.codegen.classmodel.ClassModel;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
class AptFiler implements CodegenFiler {
private final Filer filer;
private final String indent;
@@ -83,6 +88,25 @@ public Path writeResource(byte[] resource, String location, Object... originatin
}
}
+ @Override
+ public FilerTextResource textResource(String location, Object... originatingElements) {
+
+ try {
+ var resource = filer.getResource(StandardLocation.CLASS_OUTPUT, "", location);
+ List lines = new ArrayList<>();
+
+ try (var br = new BufferedReader(new InputStreamReader(resource.openInputStream(), UTF_8))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ }
+ return new FilerTextResourceImpl(filer, location, toElements(originatingElements), resource, lines);
+ } catch (IOException e) {
+ return new FilerTextResourceImpl(filer, location, toElements(originatingElements));
+ }
+ }
+
private Object originatingElement(Element[] elements, Object alternative) {
if (elements.length == 0) {
return alternative;
diff --git a/codegen/apt/src/main/java/io/helidon/codegen/apt/AptTypeInfoFactory.java b/codegen/apt/src/main/java/io/helidon/codegen/apt/AptTypeInfoFactory.java
index 4203495ee7d..d044bc99704 100644
--- a/codegen/apt/src/main/java/io/helidon/codegen/apt/AptTypeInfoFactory.java
+++ b/codegen/apt/src/main/java/io/helidon/codegen/apt/AptTypeInfoFactory.java
@@ -152,11 +152,124 @@ public static Optional create(AptContext ctx,
* {@link io.helidon.common.types.ElementKind#CONSTRUCTOR}, or {@link io.helidon.common.types.ElementKind#PARAMETER}
* then this method may return empty.
*
+ * @param ctx annotation processing context
+ * @param processedType the type that is being processed, to avoid infinite loop when looking for inherited annotations
+ * @param v the element (from annotation processing)
+ * @param elements the elements
+ * @return the created instance
+ */
+ public static Optional createTypedElementInfoFromElement(AptContext ctx,
+ TypeName processedType,
+ Element v,
+ Elements elements) {
+ TypeName type = AptTypeFactory.createTypeName(v).orElse(null);
+ TypeMirror typeMirror = null;
+ String defaultValue = null;
+ List params = List.of();
+ List componentTypeNames = List.of();
+ List elementTypeAnnotations = List.of();
+
+ Set modifierNames = v.getModifiers()
+ .stream()
+ .map(Modifier::toString)
+ .collect(Collectors.toSet());
+ Set thrownChecked = Set.of();
+
+ if (v instanceof ExecutableElement ee) {
+ typeMirror = Objects.requireNonNull(ee.getReturnType());
+ params = ee.getParameters().stream()
+ .map(it -> createTypedElementInfoFromElement(ctx, processedType, it, elements).orElseThrow(() -> {
+ return new CodegenException("Failed to create element info for parameter: " + it + ", either it uses "
+ + "invalid type, or it was removed by an element mapper. This would"
+ + " result in an invalid TypeInfo model.",
+ it);
+ }))
+ .toList();
+ AnnotationValue annotationValue = ee.getDefaultValue();
+ defaultValue = (annotationValue == null) ? null
+ : String.valueOf(annotationValue.accept(new ToAnnotationValueVisitor(elements)
+ .mapBooleanToNull(true)
+ .mapVoidToNull(true)
+ .mapBlankArrayToNull(true)
+ .mapEmptyStringToNull(true)
+ .mapToSourceDeclaration(true), null));
+
+ thrownChecked = ee.getThrownTypes()
+ .stream()
+ .filter(it -> isCheckedException(ctx, it))
+ .flatMap(it -> AptTypeFactory.createTypeName(it).stream())
+ .collect(Collectors.toSet());
+ } else if (v instanceof VariableElement ve) {
+ typeMirror = Objects.requireNonNull(ve.asType());
+ }
+
+ if (typeMirror != null) {
+ if (type == null) {
+ Element element = ctx.aptEnv().getTypeUtils().asElement(typeMirror);
+ if (element instanceof TypeElement typeElement) {
+ type = AptTypeFactory.createTypeName(typeElement, typeMirror)
+ .orElse(createFromGenericDeclaration(typeMirror.toString()));
+ } else {
+ type = AptTypeFactory.createTypeName(typeMirror)
+ .orElse(createFromGenericDeclaration(typeMirror.toString()));
+ }
+ }
+ if (typeMirror instanceof DeclaredType) {
+ List extends TypeMirror> args = ((DeclaredType) typeMirror).getTypeArguments();
+ componentTypeNames = args.stream()
+ .map(AptTypeFactory::createTypeName)
+ .filter(Optional::isPresent)
+ .map(Optional::orElseThrow)
+ .collect(Collectors.toList());
+ elementTypeAnnotations =
+ createAnnotations(ctx, ((DeclaredType) typeMirror).asElement(), elements);
+ }
+ }
+ String javadoc = ctx.aptEnv().getElementUtils().getDocComment(v);
+ javadoc = javadoc == null || javadoc.isBlank() ? "" : javadoc;
+
+ List annotations = createAnnotations(ctx, v, elements);
+ List inheritedAnnotations = createInheritedAnnotations(ctx, processedType.genericTypeName(), annotations);
+
+ TypedElementInfo.Builder builder = TypedElementInfo.builder()
+ .description(javadoc)
+ .typeName(type)
+ .componentTypes(componentTypeNames)
+ .elementName(v.getSimpleName().toString())
+ .kind(kind(v.getKind()))
+ .annotations(annotations)
+ .elementTypeAnnotations(elementTypeAnnotations)
+ .inheritedAnnotations(inheritedAnnotations)
+ .elementModifiers(modifiers(ctx, modifierNames))
+ .accessModifier(accessModifier(modifierNames))
+ .throwsChecked(thrownChecked)
+ .parameterArguments(params)
+ .originatingElement(v);
+ AptTypeFactory.createTypeName(v.getEnclosingElement()).ifPresent(builder::enclosingType);
+ Optional.ofNullable(defaultValue).ifPresent(builder::defaultValue);
+
+ return mapElement(ctx, builder.build());
+ }
+
+ /**
+ * Creates an instance of a {@link io.helidon.common.types.TypedElementInfo} given its type and variable element from
+ * annotation processing. If the passed in element is not a {@link io.helidon.common.types.ElementKind#FIELD},
+ * {@link io.helidon.common.types.ElementKind#METHOD},
+ * {@link io.helidon.common.types.ElementKind#CONSTRUCTOR}, or {@link io.helidon.common.types.ElementKind#PARAMETER}
+ * then this method may return empty.
+ *
+ * This method does not include inherited annotations.
+ *
* @param ctx annotation processing context
* @param v the element (from annotation processing)
* @param elements the elements
* @return the created instance
+ * @deprecated use
+ * {@link #createTypedElementInfoFromElement(AptContext, io.helidon.common.types.TypeName,
+ * javax.lang.model.element.Element, javax.lang.model.util.Elements)}
+ * instead
*/
+ @Deprecated(since = "4.0.10", forRemoval = true)
public static Optional createTypedElementInfoFromElement(AptContext ctx,
Element v,
Elements elements) {
@@ -289,10 +402,11 @@ private static Optional create(AptContext ctx,
Elements elementUtils = ctx.aptEnv().getElementUtils();
try {
- List annotations =
- List.copyOf(createAnnotations(ctx,
- elementUtils.getTypeElement(genericTypeName.resolvedName()),
- elementUtils));
+ List annotations = createAnnotations(ctx,
+ elementUtils.getTypeElement(genericTypeName.resolvedName()),
+ elementUtils);
+ List inheritedAnnotations = createInheritedAnnotations(ctx, genericTypeName, annotations);
+
Set annotationsOnTypeOrElements = new HashSet<>();
annotations.stream()
.map(Annotation::typeName)
@@ -302,7 +416,7 @@ private static Optional create(AptContext ctx,
List otherElements = new ArrayList<>();
typeElement.getEnclosedElements()
.stream()
- .flatMap(it -> createTypedElementInfoFromElement(ctx, it, elementUtils).stream())
+ .flatMap(it -> createTypedElementInfoFromElement(ctx, genericTypeName, it, elementUtils).stream())
.forEach(it -> {
if (elementPredicate.test(it)) {
elementsWeCareAbout.add(it);
@@ -326,6 +440,7 @@ private static Optional create(AptContext ctx,
.typeName(typeName)
.kind(kind(typeElement.getKind()))
.annotations(annotations)
+ .inheritedAnnotations(inheritedAnnotations)
.elementModifiers(modifiers(ctx, modifiers))
.accessModifier(accessModifier(modifiers))
.elementInfo(elementsWeCareAbout)
@@ -436,6 +551,69 @@ private static List createAnnotations(AptContext ctx, Element elemen
.toList();
}
+ private static List createInheritedAnnotations(AptContext ctx,
+ TypeName processedType,
+ List elementAnnotations) {
+ List result = new ArrayList<>();
+ Set processedTypes = new HashSet<>();
+
+ // for each annotation on this type, find its type info, and collect all meta annotations marked as @Inherited
+ for (Annotation elementAnnotation : elementAnnotations) {
+ if (processedType.equals(elementAnnotation.typeName())) {
+ // self reference
+ continue;
+ }
+ addInherited(ctx, result, processedTypes, elementAnnotation, false);
+ }
+ return result;
+ }
+
+ private static void addInherited(AptContext ctx,
+ List result,
+ Set processedTypes,
+ Annotation annotation,
+ boolean shouldAdd) {
+ TypeName annotationType = annotation.typeName();
+ if (!processedTypes.add(annotationType)) {
+ return;
+ }
+
+ if (!annotationFilter(annotation)) {
+ return;
+ }
+
+ if (annotationType.equals(TypeNames.INHERITED)) {
+ return;
+ }
+
+ Optional found = create(ctx, annotationType, it -> false);
+
+ if (found.isEmpty()) {
+ ctx.logger().log(System.Logger.Level.DEBUG, "Annotation " + annotationType
+ + " not available, cannot obtain inherited annotations");
+ return;
+ }
+
+ TypeInfo annoTypeInfo = found.get();
+
+ if (annoTypeInfo.hasAnnotation(TypeNames.INHERITED)) {
+ // first level annotations should not be added as inherited
+ if (shouldAdd) {
+ // add this annotation
+ result.add(annotation);
+ }
+
+ // check my meta annotations
+ for (Annotation metaAnnotation : annoTypeInfo.annotations()) {
+ // if self annotated, ignore
+ if (annotationType.equals(metaAnnotation.typeName())) {
+ continue;
+ }
+ addInherited(ctx, result, processedTypes, metaAnnotation, true);
+ }
+ }
+ }
+
/**
* Converts the provided modifiers to the corresponding set of modifier names.
*
diff --git a/codegen/apt/src/main/java/io/helidon/codegen/apt/FilerTextResourceImpl.java b/codegen/apt/src/main/java/io/helidon/codegen/apt/FilerTextResourceImpl.java
new file mode 100644
index 00000000000..f0cfb8ed4f6
--- /dev/null
+++ b/codegen/apt/src/main/java/io/helidon/codegen/apt/FilerTextResourceImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2024 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.codegen.apt;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.Element;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+import io.helidon.codegen.CodegenException;
+import io.helidon.codegen.FilerTextResource;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+class FilerTextResourceImpl implements FilerTextResource {
+ private final Filer filer;
+ private final String location;
+ private final Element[] originatingElements;
+ private final FileObject originalResource; // may be null
+ private final List currentLines;
+
+ private boolean modified;
+
+ FilerTextResourceImpl(Filer filer, String location, Element[] originatingElements) {
+ this.filer = filer;
+ this.location = location;
+ this.originatingElements = originatingElements;
+ this.originalResource = null;
+ this.currentLines = new ArrayList<>();
+ }
+
+ FilerTextResourceImpl(Filer filer,
+ String location,
+ Element[] originatingElements,
+ FileObject originalResource,
+ List existingLines) {
+ this.filer = filer;
+ this.location = location;
+ this.originatingElements = originatingElements;
+ this.originalResource = originalResource;
+ this.currentLines = new ArrayList<>(existingLines);
+ }
+
+ @Override
+ public List lines() {
+ return List.copyOf(currentLines);
+ }
+
+ @Override
+ public void lines(List newLines) {
+ currentLines.clear();
+ currentLines.addAll(newLines);
+ modified = true;
+ }
+
+ @Override
+ public void write() {
+ if (modified) {
+ if (originalResource != null) {
+ originalResource.delete();
+ }
+ try {
+ FileObject newResource = filer.createResource(StandardLocation.CLASS_OUTPUT,
+ "",
+ location,
+ originatingElements);
+ try (var pw = new PrintWriter(new OutputStreamWriter(newResource.openOutputStream(), UTF_8))) {
+ for (String line : currentLines) {
+ pw.println(line);
+ }
+ }
+ } catch (Exception e) {
+ throw new CodegenException("Failed to create resource: " + location, e);
+ }
+ }
+ }
+}
diff --git a/codegen/class-model/src/main/java/io/helidon/codegen/classmodel/ContentSupport.java b/codegen/class-model/src/main/java/io/helidon/codegen/classmodel/ContentSupport.java
index d8725fab180..58b513935c3 100644
--- a/codegen/class-model/src/main/java/io/helidon/codegen/classmodel/ContentSupport.java
+++ b/codegen/class-model/src/main/java/io/helidon/codegen/classmodel/ContentSupport.java
@@ -66,6 +66,12 @@ static void addCreateElement(ContentBuilder> contentBuilder, TypedElementInfo
.addContentLine(")");
}
+ for (Annotation annotation : element.inheritedAnnotations()) {
+ contentBuilder.addContent(".addInheritedAnnotation(")
+ .addContentCreate(annotation)
+ .addContentLine(")");
+ }
+
AccessModifier accessModifier = element.accessModifier();
if (accessModifier != AccessModifier.PACKAGE_PRIVATE) {
contentBuilder.addContent(".accessModifier(")
diff --git a/codegen/codegen/src/main/java/io/helidon/codegen/CodegenFiler.java b/codegen/codegen/src/main/java/io/helidon/codegen/CodegenFiler.java
index a54669f0c83..f4b2ab46ac2 100644
--- a/codegen/codegen/src/main/java/io/helidon/codegen/CodegenFiler.java
+++ b/codegen/codegen/src/main/java/io/helidon/codegen/CodegenFiler.java
@@ -16,10 +16,12 @@
package io.helidon.codegen;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import io.helidon.codegen.classmodel.ClassModel;
@@ -45,11 +47,23 @@ public interface CodegenFiler {
*
* @param resource bytes of the resource file
* @param location location to write to in the classes output directory
- * @param originatingElements elements that caused this type to be generated
+ * @param originatingElements elements that caused this file to be generated
* @return written path, we expect to always run on local file system
*/
Path writeResource(byte[] resource, String location, Object... originatingElements);
+ /**
+ * A text resource that can be updated in the output.
+ * Note that the resource can only be written once per processing round.
+ *
+ * @param location location to read/write to in the classes output directory
+ * @param originatingElements elements that caused this file to be generated
+ * @return the resource that can be used to update the file
+ */
+ default FilerTextResource textResource(String location, Object... originatingElements) {
+ throw new UnsupportedOperationException("Method textResource not implemented yet on " + getClass().getName());
+ }
+
/**
* Write a {@code META-INF/services} file for a specific provider interface and implementation(s).
*
@@ -66,25 +80,40 @@ default void services(TypeName generator,
Objects.requireNonNull(providerInterface);
Objects.requireNonNull(providers);
- String location = "META-INF/services/" + providerInterface.fqName();
if (providers.isEmpty()) {
- throw new CodegenException("List of providers is empty, cannot generate " + location);
+ // do not write services file if there is no provider added
+ return;
+ }
+
+ String location = "META-INF/services/" + providerInterface.fqName();
+
+ var resource = textResource(location, originatingElements);
+
+ List lines = new ArrayList<>(resource.lines());
+ Set existingServices = lines.stream()
+ .map(String::trim)
+ .filter(Predicate.not(it -> it.startsWith("#")))
+ .map(TypeName::create)
+ .collect(Collectors.toSet());
+
+ if (lines.isEmpty()) {
+ // @Generated
+ lines.add("# " + GeneratedAnnotationHandler.create(generator,
+ providers.getFirst(),
+ TypeName.create(
+ "MetaInfServicesModuleComponent"),
+ "1",
+ ""));
+ }
+
+ for (TypeName provider : providers) {
+ if (existingServices.add(provider)) {
+ // only add the provider if it does not yet exist
+ lines.add(provider.fqName());
+ }
}
- byte[] resourceBytes = (
- "# " + GeneratedAnnotationHandler.create(generator,
- providers.getFirst(),
- TypeName.create(
- "MetaInfServicesModuleComponent"),
- "1",
- "")
- + "\n"
- + providers.stream()
- .map(TypeName::declaredName)
- .collect(Collectors.joining("\n")))
- .getBytes(StandardCharsets.UTF_8);
- writeResource(resourceBytes,
- location,
- originatingElements);
+ resource.lines(lines);
+ resource.write();
}
}
diff --git a/codegen/codegen/src/main/java/io/helidon/codegen/FilerTextResource.java b/codegen/codegen/src/main/java/io/helidon/codegen/FilerTextResource.java
new file mode 100644
index 00000000000..bee2e8f007f
--- /dev/null
+++ b/codegen/codegen/src/main/java/io/helidon/codegen/FilerTextResource.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2024 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.codegen;
+
+import java.util.List;
+
+/**
+ * A resource from output (such as {@code target/META-INF/helidon}) that can have existing
+ * values, and may be replaced with a new value.
+ */
+public interface FilerTextResource {
+ /**
+ * Existing lines of the resource. Returns an empty list if the resource does not exist.
+ *
+ * @return list of lines, immutable collection
+ */
+ List lines();
+
+ /**
+ * New lines of the resource.
+ *
+ * @param newLines new lines to {@link #write()} to the resource file
+ */
+ void lines(List newLines);
+
+ /**
+ * Writes the new lines to the output. This operation can only be called once per codegen round.
+ */
+ void write();
+}
diff --git a/common/tls/pom.xml b/common/tls/pom.xml
index 71b72195c11..7934038f4dd 100644
--- a/common/tls/pom.xml
+++ b/common/tls/pom.xml
@@ -32,20 +32,9 @@
etc/spotbugs/exclude.xml
-
- false
-
- helidon-inject-api
- io.helidon.inject
- true
- io.helidon.commonhelidon-common
@@ -61,6 +50,10 @@
io.helidon.builderhelidon-builder-api
+
+
+ io.helidon.service
+ helidon-service-registrytrue
diff --git a/common/tls/src/main/java/io/helidon/common/tls/TlsManager.java b/common/tls/src/main/java/io/helidon/common/tls/TlsManager.java
index 83be02abee0..2f4c26ee810 100644
--- a/common/tls/src/main/java/io/helidon/common/tls/TlsManager.java
+++ b/common/tls/src/main/java/io/helidon/common/tls/TlsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -23,7 +23,7 @@
import javax.net.ssl.X509TrustManager;
import io.helidon.common.config.NamedService;
-import io.helidon.inject.api.Contract;
+import io.helidon.service.registry.Service;
/**
* Implementors of this contract are responsible for managing the {@link javax.net.ssl.SSLContext} instance lifecycle, as well
@@ -32,7 +32,7 @@
*
* How context changes are observed is based upon the implementation of the manager.
*/
-@Contract
+@Service.Contract
public interface TlsManager extends NamedService {
/**
diff --git a/common/tls/src/main/java/module-info.java b/common/tls/src/main/java/module-info.java
index b48537cc15b..8bdffb196b8 100644
--- a/common/tls/src/main/java/module-info.java
+++ b/common/tls/src/main/java/module-info.java
@@ -18,7 +18,8 @@
* TLS configuration for client and server.
*/
module io.helidon.common.tls {
- requires static io.helidon.inject.api;
+ // only annotation, no generated code
+ requires static io.helidon.service.registry;
requires io.helidon.builder.api;
requires io.helidon.common;
diff --git a/common/types/src/main/java/io/helidon/common/types/Annotated.java b/common/types/src/main/java/io/helidon/common/types/Annotated.java
index 3be2ab6f8ae..07f13ac5c29 100644
--- a/common/types/src/main/java/io/helidon/common/types/Annotated.java
+++ b/common/types/src/main/java/io/helidon/common/types/Annotated.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -16,6 +16,7 @@
package io.helidon.common.types;
+import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
@@ -27,14 +28,38 @@
*/
public interface Annotated {
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @return the list of annotations on this element
+ * @return the list of annotations declared on this element
*/
@Option.Singular
List annotations();
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @return list of all meta annotations of this element
+ */
+ @Option.Singular
+ List inheritedAnnotations();
+
+ /**
+ * List of all annotations - both {@link #annotations()} and {@link #inheritedAnnotations()}.
+ *
+ * @return list of all annotations valid for this element
+ */
+ default List allAnnotations() {
+ List result = new ArrayList<>(annotations());
+ result.addAll(inheritedAnnotations());
+ return List.copyOf(result);
+ }
+
/**
* Find an annotation on this annotated type.
*
diff --git a/common/types/src/main/java/io/helidon/common/types/TypeInfo.java b/common/types/src/main/java/io/helidon/common/types/TypeInfo.java
index 2a7c36c4667..d47b9f04df1 100644
--- a/common/types/src/main/java/io/helidon/common/types/TypeInfo.java
+++ b/common/types/src/main/java/io/helidon/common/types/TypeInfo.java
@@ -66,6 +66,7 @@ static TypeInfo.Builder builder(TypeInfo instance) {
abstract class BuilderBase, PROTOTYPE extends TypeInfo> implements Prototype.Builder {
private final List annotations = new ArrayList<>();
+ private final List inheritedAnnotations = new ArrayList<>();
private final List interfaceTypeInfo = new ArrayList<>();
private final List elementInfo = new ArrayList<>();
private final List otherElementInfo = new ArrayList<>();
@@ -111,6 +112,7 @@ public BUILDER from(TypeInfo prototype) {
module(prototype.module());
originatingElement(prototype.originatingElement());
addAnnotations(prototype.annotations());
+ addInheritedAnnotations(prototype.inheritedAnnotations());
return self();
}
@@ -137,6 +139,7 @@ public BUILDER from(TypeInfo.BuilderBase, ?> builder) {
builder.module().ifPresent(this::module);
builder.originatingElement().ifPresent(this::originatingElement);
addAnnotations(builder.annotations());
+ addInheritedAnnotations(builder.inheritedAnnotations());
return self();
}
@@ -731,10 +734,11 @@ public BUILDER originatingElement(Object originatingElement) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param annotations the list of annotations on this element
+ * @param annotations the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -746,10 +750,11 @@ public BUILDER annotations(List extends Annotation> annotations) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param annotations the list of annotations on this element
+ * @param annotations the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -760,10 +765,11 @@ public BUILDER addAnnotations(List extends Annotation> annotations) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param annotation the list of annotations on this element
+ * @param annotation the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -774,10 +780,11 @@ public BUILDER addAnnotation(Annotation annotation) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param consumer the list of annotations on this element
+ * @param consumer the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -789,6 +796,77 @@ public BUILDER addAnnotation(Consumer consumer) {
return self();
}
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param inheritedAnnotations list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER inheritedAnnotations(List extends Annotation> inheritedAnnotations) {
+ Objects.requireNonNull(inheritedAnnotations);
+ this.inheritedAnnotations.clear();
+ this.inheritedAnnotations.addAll(inheritedAnnotations);
+ return self();
+ }
+
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param inheritedAnnotations list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER addInheritedAnnotations(List extends Annotation> inheritedAnnotations) {
+ Objects.requireNonNull(inheritedAnnotations);
+ this.inheritedAnnotations.addAll(inheritedAnnotations);
+ return self();
+ }
+
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param inheritedAnnotation list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER addInheritedAnnotation(Annotation inheritedAnnotation) {
+ Objects.requireNonNull(inheritedAnnotation);
+ this.inheritedAnnotations.add(inheritedAnnotation);
+ return self();
+ }
+
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param consumer list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER addInheritedAnnotation(Consumer consumer) {
+ Objects.requireNonNull(consumer);
+ var builder = Annotation.builder();
+ consumer.accept(builder);
+ this.inheritedAnnotations.add(builder.build());
+ return self();
+ }
+
/**
* The type name.
*
@@ -955,8 +1033,9 @@ public Optional originatingElement() {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
* @return the annotations
*/
@@ -964,6 +1043,19 @@ public List annotations() {
return annotations;
}
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @return the inherited annotations
+ */
+ public List inheritedAnnotations() {
+ return inheritedAnnotations;
+ }
+
@Override
public String toString() {
return "TypeInfoBuilder{"
@@ -974,7 +1066,8 @@ public String toString() {
+ "elementModifiers=" + elementModifiers + ","
+ "accessModifier=" + accessModifier + ","
+ "module=" + module + ","
- + "annotations=" + annotations
+ + "annotations=" + annotations + ","
+ + "inheritedAnnotations=" + inheritedAnnotations
+ "}";
}
@@ -1067,6 +1160,7 @@ protected static class TypeInfoImpl implements TypeInfo {
private final AccessModifier accessModifier;
private final ElementKind kind;
private final List annotations;
+ private final List inheritedAnnotations;
private final List interfaceTypeInfo;
private final List elementInfo;
private final List otherElementInfo;
@@ -1104,6 +1198,7 @@ protected TypeInfoImpl(TypeInfo.BuilderBase, ?> builder) {
this.module = builder.module();
this.originatingElement = builder.originatingElement();
this.annotations = List.copyOf(builder.annotations());
+ this.inheritedAnnotations = List.copyOf(builder.inheritedAnnotations());
}
@Override
@@ -1186,6 +1281,11 @@ public List annotations() {
return annotations;
}
+ @Override
+ public List inheritedAnnotations() {
+ return inheritedAnnotations;
+ }
+
@Override
public String toString() {
return "TypeInfo{"
@@ -1196,7 +1296,8 @@ public String toString() {
+ "elementModifiers=" + elementModifiers + ","
+ "accessModifier=" + accessModifier + ","
+ "module=" + module + ","
- + "annotations=" + annotations
+ + "annotations=" + annotations + ","
+ + "inheritedAnnotations=" + inheritedAnnotations
+ "}";
}
@@ -1215,7 +1316,8 @@ public boolean equals(Object o) {
&& Objects.equals(elementModifiers, other.elementModifiers())
&& Objects.equals(accessModifier, other.accessModifier())
&& Objects.equals(module, other.module())
- && Objects.equals(annotations, other.annotations());
+ && Objects.equals(annotations, other.annotations())
+ && Objects.equals(inheritedAnnotations, other.inheritedAnnotations());
}
@Override
@@ -1227,7 +1329,8 @@ public int hashCode() {
elementModifiers,
accessModifier,
module,
- annotations);
+ annotations,
+ inheritedAnnotations);
}
}
diff --git a/common/types/src/main/java/io/helidon/common/types/TypeNames.java b/common/types/src/main/java/io/helidon/common/types/TypeNames.java
index f4599281b45..62fe59da5a3 100644
--- a/common/types/src/main/java/io/helidon/common/types/TypeNames.java
+++ b/common/types/src/main/java/io/helidon/common/types/TypeNames.java
@@ -16,6 +16,7 @@
package io.helidon.common.types;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.time.Duration;
import java.util.Collection;
@@ -72,6 +73,10 @@ public final class TypeNames {
* Type name for {@link java.lang.annotation.Retention}.
*/
public static final TypeName RETENTION = TypeName.create(Retention.class);
+ /**
+ * Type name for {@link java.lang.annotation.Inherited}.
+ */
+ public static final TypeName INHERITED = TypeName.create(Inherited.class);
/*
Primitive types and their boxed counterparts
diff --git a/common/types/src/main/java/io/helidon/common/types/TypedElementInfo.java b/common/types/src/main/java/io/helidon/common/types/TypedElementInfo.java
index 87be9327536..905b540268f 100644
--- a/common/types/src/main/java/io/helidon/common/types/TypedElementInfo.java
+++ b/common/types/src/main/java/io/helidon/common/types/TypedElementInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -72,6 +72,7 @@ abstract class BuilderBase annotations = new ArrayList<>();
private final List elementTypeAnnotations = new ArrayList<>();
+ private final List inheritedAnnotations = new ArrayList<>();
private final List componentTypes = new ArrayList<>();
private final List parameterArguments = new ArrayList<>();
private final Set throwsChecked = new LinkedHashSet<>();
@@ -116,6 +117,7 @@ public BUILDER from(TypedElementInfo prototype) {
addThrowsChecked(prototype.throwsChecked());
originatingElement(prototype.originatingElement());
addAnnotations(prototype.annotations());
+ addInheritedAnnotations(prototype.inheritedAnnotations());
return self();
}
@@ -142,6 +144,7 @@ public BUILDER from(TypedElementInfo.BuilderBase, ?> builder) {
addThrowsChecked(builder.throwsChecked());
builder.originatingElement().ifPresent(this::originatingElement);
addAnnotations(builder.annotations());
+ addInheritedAnnotations(builder.inheritedAnnotations());
return self();
}
@@ -596,10 +599,11 @@ public BUILDER originatingElement(Object originatingElement) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param annotations the list of annotations on this element
+ * @param annotations the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -611,10 +615,11 @@ public BUILDER annotations(List extends Annotation> annotations) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param annotations the list of annotations on this element
+ * @param annotations the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -625,10 +630,11 @@ public BUILDER addAnnotations(List extends Annotation> annotations) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param annotation the list of annotations on this element
+ * @param annotation the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -639,10 +645,11 @@ public BUILDER addAnnotation(Annotation annotation) {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
- * @param consumer the list of annotations on this element
+ * @param consumer the list of annotations declared on this element
* @return updated builder instance
* @see #annotations()
*/
@@ -654,6 +661,77 @@ public BUILDER addAnnotation(Consumer consumer) {
return self();
}
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param inheritedAnnotations list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER inheritedAnnotations(List extends Annotation> inheritedAnnotations) {
+ Objects.requireNonNull(inheritedAnnotations);
+ this.inheritedAnnotations.clear();
+ this.inheritedAnnotations.addAll(inheritedAnnotations);
+ return self();
+ }
+
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param inheritedAnnotations list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER addInheritedAnnotations(List extends Annotation> inheritedAnnotations) {
+ Objects.requireNonNull(inheritedAnnotations);
+ this.inheritedAnnotations.addAll(inheritedAnnotations);
+ return self();
+ }
+
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param inheritedAnnotation list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER addInheritedAnnotation(Annotation inheritedAnnotation) {
+ Objects.requireNonNull(inheritedAnnotation);
+ this.inheritedAnnotations.add(inheritedAnnotation);
+ return self();
+ }
+
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @param consumer list of all meta annotations of this element
+ * @return updated builder instance
+ * @see #inheritedAnnotations()
+ */
+ public BUILDER addInheritedAnnotation(Consumer consumer) {
+ Objects.requireNonNull(consumer);
+ var builder = Annotation.builder();
+ consumer.accept(builder);
+ this.inheritedAnnotations.add(builder.build());
+ return self();
+ }
+
/**
* Description, such as javadoc, if available.
*
@@ -811,8 +889,9 @@ public Optional originatingElement() {
}
/**
- * The list of known annotations for this element. Note that "known" implies that the annotation is visible, which depends
- * upon the context in which it was build.
+ * List of declared and known annotations for this element.
+ * Note that "known" implies that the annotation is visible, which depends
+ * upon the context in which it was build (such as the {@link java.lang.annotation.Retention of the annotation}).
*
* @return the annotations
*/
@@ -820,6 +899,19 @@ public List annotations() {
return annotations;
}
+ /**
+ * List of all inherited annotations for this element. Inherited annotations are annotations declared
+ * on annotations of this element that are also marked as {@link java.lang.annotation.Inherited}.
+ *
+ * The returned list does not contain {@link #annotations()}. If a meta-annotation is present on multiple
+ * annotations, it will be returned once for each such declaration.
+ *
+ * @return the inherited annotations
+ */
+ public List inheritedAnnotations() {
+ return inheritedAnnotations;
+ }
+
/**
* Handles providers and decorators.
*/
@@ -916,6 +1008,7 @@ protected static class TypedElementInfoImpl implements TypedElementInfo {
private final ElementKind kind;
private final List annotations;
private final List elementTypeAnnotations;
+ private final List inheritedAnnotations;
private final List componentTypes;
private final List parameterArguments;
private final Optional enclosingType;
@@ -951,6 +1044,7 @@ protected TypedElementInfoImpl(TypedElementInfo.BuilderBase, ?> builder) {
this.throwsChecked = Collections.unmodifiableSet(new LinkedHashSet<>(builder.throwsChecked()));
this.originatingElement = builder.originatingElement();
this.annotations = List.copyOf(builder.annotations());
+ this.inheritedAnnotations = List.copyOf(builder.inheritedAnnotations());
}
@Override
@@ -1043,6 +1137,11 @@ public List annotations() {
return annotations;
}
+ @Override
+ public List inheritedAnnotations() {
+ return inheritedAnnotations;
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) {
@@ -1057,12 +1156,20 @@ public boolean equals(Object o) {
&& Objects.equals(enclosingType, other.enclosingType())
&& Objects.equals(parameterArguments, other.parameterArguments())
&& Objects.equals(throwsChecked, other.throwsChecked())
- && Objects.equals(annotations, other.annotations());
+ && Objects.equals(annotations, other.annotations())
+ && Objects.equals(inheritedAnnotations, other.inheritedAnnotations());
}
@Override
public int hashCode() {
- return Objects.hash(typeName, elementName, kind, enclosingType, parameterArguments, throwsChecked, annotations);
+ return Objects.hash(typeName,
+ elementName,
+ kind,
+ enclosingType,
+ parameterArguments,
+ throwsChecked,
+ annotations,
+ inheritedAnnotations);
}
}
diff --git a/config/config/pom.xml b/config/config/pom.xml
index 44090a3f54f..e788c3c70b2 100644
--- a/config/config/pom.xml
+++ b/config/config/pom.xml
@@ -51,12 +51,12 @@
helidon-common-media-type
@@ -232,7 +233,6 @@
* throws {@link ConfigMappingException}, unless you use the config beans support,
* that can handle classes that fulfill some requirements (see documentation), such as a public constructor,
* static "create(Config)" method etc.
- *
* A {@code Config} instance, including the default {@code Config} returned by
@@ -240,6 +240,7 @@
* config system merges these together so that values from config sources with higher {@link io.helidon.common.Weight weight}
* have priority over values from config sources with lower weight.
*/
+@Service.Contract
public interface Config extends io.helidon.common.config.Config {
/**
* Generic type of configuration.
diff --git a/config/config/src/main/java/io/helidon/config/ConfigProducer.java b/config/config/src/main/java/io/helidon/config/ConfigProvider.java
similarity index 68%
rename from config/config/src/main/java/io/helidon/config/ConfigProducer.java
rename to config/config/src/main/java/io/helidon/config/ConfigProvider.java
index 5db55517f1f..d6ed2e86620 100644
--- a/config/config/src/main/java/io/helidon/config/ConfigProducer.java
+++ b/config/config/src/main/java/io/helidon/config/ConfigProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 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.
@@ -19,34 +19,44 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
+import java.util.function.Supplier;
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.ConfigFilter;
+import io.helidon.config.spi.ConfigMapperProvider;
+import io.helidon.config.spi.ConfigParser;
import io.helidon.config.spi.ConfigSource;
-import io.helidon.inject.api.ExternalContracts;
+import io.helidon.service.registry.Service;
-import jakarta.inject.Inject;
-import jakarta.inject.Provider;
-import jakarta.inject.Singleton;
-
-@Singleton
-@ExternalContracts(Config.class)
-class ConfigProducer implements Config {
+@Service.Provider
+@Service.ExternalContracts(Config.class)
+class ConfigProvider implements Config {
private final Config config;
- @Inject
- ConfigProducer(List> serviceProviders) {
+ ConfigProvider(Supplier metaConfig,
+ Supplier> configSources,
+ Supplier> configParsers,
+ Supplier> configFilters,
+ Supplier> configMappers) {
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)
+ .config(metaConfig.get().metaConfiguration())
+ .update(it -> configSources.get()
.forEach(it::addSource))
+ .disableParserServices()
+ .update(it -> configParsers.get()
+ .forEach(it::addParser))
+ .disableFilterServices()
+ .update(it -> configFilters.get()
+ .forEach(it::addFilter))
+ .disableMapperServices()
+ .update(it -> configMappers.get()
+ .forEach(it::addMapper))
.build();
}
}
diff --git a/config/config/src/main/java/io/helidon/config/MetaConfig.java b/config/config/src/main/java/io/helidon/config/MetaConfig.java
index d96bd48fc5f..afbe1b9c502 100644
--- a/config/config/src/main/java/io/helidon/config/MetaConfig.java
+++ b/config/config/src/main/java/io/helidon/config/MetaConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2024 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.
@@ -32,6 +32,7 @@
import io.helidon.config.spi.OverrideSource;
import io.helidon.config.spi.PollingStrategy;
import io.helidon.config.spi.RetryPolicy;
+import io.helidon.service.registry.Service;
/**
* Meta configuration.
@@ -69,6 +70,7 @@
*
Classpath resource config source for resource {@code default.yaml} that is mandatory
*
*/
+@Service.Provider
public final class MetaConfig {
private static final System.Logger LOGGER = System.getLogger(MetaConfig.class.getName());
private static final Set SUPPORTED_MEDIA_TYPES;
@@ -88,7 +90,10 @@ public final class MetaConfig {
SUPPORTED_SUFFIXES = List.copyOf(supportedSuffixes);
}
- private MetaConfig() {
+ private final Config metaConfig;
+
+ MetaConfig() {
+ this.metaConfig = metaConfig().orElseGet(Config::empty);
}
/**
@@ -201,7 +206,15 @@ public static List configSource(Config sourceMetaConfig) {
return List.of(source);
}
+ }
+ /**
+ * Meta configuration if provided, or empty config if not.
+ *
+ * @return meta configuration
+ */
+ public Config metaConfiguration() {
+ return this.metaConfig;
}
// override config source
diff --git a/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java b/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java
index f6994eea604..80b237ca605 100644
--- a/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java
+++ b/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java
@@ -18,6 +18,7 @@
import io.helidon.config.Config;
import io.helidon.config.ConfigItem;
+import io.helidon.service.registry.Service;
/**
* Filter that can transform elementary configuration ({@code String}) values
@@ -53,6 +54,7 @@
* @see Config.Builder#addFilter(java.util.function.Function)
*/
@FunctionalInterface
+@Service.Contract
public interface ConfigFilter {
/**
diff --git a/config/config/src/main/java/io/helidon/config/spi/ConfigMapperProvider.java b/config/config/src/main/java/io/helidon/config/spi/ConfigMapperProvider.java
index 7d0be9cccf7..c35c8b0e0cd 100644
--- a/config/config/src/main/java/io/helidon/config/spi/ConfigMapperProvider.java
+++ b/config/config/src/main/java/io/helidon/config/spi/ConfigMapperProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2024 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.
@@ -23,6 +23,7 @@
import io.helidon.common.GenericType;
import io.helidon.config.Config;
+import io.helidon.service.registry.Service;
/**
* Provides mapping functions that convert a {@code Config}
@@ -43,6 +44,7 @@
* @see Config.Builder#disableMapperServices()
*/
@FunctionalInterface
+@Service.Contract
public interface ConfigMapperProvider {
/**
diff --git a/config/config/src/main/java/io/helidon/config/spi/ConfigParser.java b/config/config/src/main/java/io/helidon/config/spi/ConfigParser.java
index bda0970a675..2413f0e61ab 100644
--- a/config/config/src/main/java/io/helidon/config/spi/ConfigParser.java
+++ b/config/config/src/main/java/io/helidon/config/spi/ConfigParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2024 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.
@@ -27,6 +27,7 @@
import io.helidon.common.media.type.MediaType;
import io.helidon.config.spi.ConfigNode.ObjectNode;
+import io.helidon.service.registry.Service;
/**
* Transforms config {@link io.helidon.config.spi.ConfigParser.Content} into a {@link ConfigNode.ObjectNode} that
@@ -49,6 +50,7 @@
* @see io.helidon.config.spi.ParsableSource
* @see io.helidon.config.ConfigParsers ConfigParsers - access built-in implementations.
*/
+@Service.Contract
public interface ConfigParser {
/**
* Returns set of supported media types by the parser.
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 59829aa6974..0a89530f30d 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, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2024 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,7 +20,7 @@
import io.helidon.config.Config;
import io.helidon.config.ConfigSources;
-import io.helidon.inject.api.Contract;
+import io.helidon.service.registry.Service;
/**
* {@link Source} of configuration.
@@ -63,7 +63,7 @@
* @see io.helidon.config.AbstractConfigSource
* @see ConfigSources ConfigSources - access built-in implementations.
*/
-@Contract
+@Service.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 35e64e3bf5b..03790f9abcf 100644
--- a/config/config/src/main/java/module-info.java
+++ b/config/config/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2024 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.
@@ -27,16 +27,13 @@
in = HelidonFlavor.SE
)
module io.helidon.config {
-
- requires io.helidon.inject.api;
-
requires static io.helidon.common.features.api;
- requires static io.helidon.inject.runtime;
- requires static jakarta.inject;
+ requires static io.helidon.service.registry;
requires transitive io.helidon.common.config;
requires transitive io.helidon.common.media.type;
requires transitive io.helidon.common;
+ requires transitive io.helidon.builder.api;
exports io.helidon.config;
exports io.helidon.config.spi;
@@ -54,8 +51,6 @@
with io.helidon.config.PropertiesConfigParser;
provides io.helidon.common.config.spi.ConfigProvider
with io.helidon.config.HelidonConfigProvider;
- provides io.helidon.inject.api.ModuleComponent
- with io.helidon.config.Injection$$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/config/src/main/resources/META-INF/helidon/service.loader b/config/config/src/main/resources/META-INF/helidon/service.loader
new file mode 100644
index 00000000000..d07172ed9b3
--- /dev/null
+++ b/config/config/src/main/resources/META-INF/helidon/service.loader
@@ -0,0 +1,4 @@
+# List of service contracts we want to support either from service registry, or from service loader
+io.helidon.config.spi.ConfigParser
+io.helidon.config.spi.ConfigFilter
+io.helidon.config.spi.ConfigMapperProvider
diff --git a/config/tests/config-metadata-builder-api/pom.xml b/config/tests/config-metadata-builder-api/pom.xml
index 7e6d088a01c..a1ffd5d69a8 100644
--- a/config/tests/config-metadata-builder-api/pom.xml
+++ b/config/tests/config-metadata-builder-api/pom.xml
@@ -77,9 +77,14 @@
helidon-config-metadata-processor${helidon.version}
+
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+ io.helidon.builder
- helidon-builder-processor
+ helidon-builder-codegen${helidon.version}
@@ -90,9 +95,14 @@
helidon-config-metadata-processor${helidon.version}
+
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+ io.helidon.builder
- helidon-builder-processor
+ helidon-builder-codegen${helidon.version}
diff --git a/config/tests/service-registry/pom.xml b/config/tests/service-registry/pom.xml
index bcda355898b..09310504d0b 100644
--- a/config/tests/service-registry/pom.xml
+++ b/config/tests/service-registry/pom.xml
@@ -35,23 +35,14 @@
helidon-config
- jakarta.inject
- jakarta.inject-api
-
-
- io.helidon.inject
- helidon-inject-runtime
+ io.helidon.service
+ helidon-service-registryio.helidon.confighelidon-config-yamltest
-
- io.helidon.inject
- helidon-inject-testing
- test
- org.junit.jupiterjunit-jupiter-api
@@ -70,31 +61,38 @@
org.apache.maven.pluginsmaven-compiler-plugin
-
- -Ainject.acceptPreview=true
-
- io.helidon.inject
- helidon-inject-processor
+ io.helidon.codegen
+ helidon-codegen-apt${helidon.version}
- io.helidon.common.processor
- helidon-common-processor-helidon-copyright
+ io.helidon.service
+ helidon-service-codegen
+ ${helidon.version}
+
+
+ io.helidon.codegen
+ helidon-codegen-helidon-copyright${helidon.version}
- io.helidon.inject
- helidon-inject-processor
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-codegen${helidon.version}
- io.helidon.common.processor
- helidon-common-processor-helidon-copyright
+ io.helidon.codegen
+ helidon-codegen-helidon-copyright${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
index 19c0badf958..dbd7ebaf6da 100644
--- 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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,9 +20,7 @@
import io.helidon.common.config.Config;
import io.helidon.common.config.GlobalConfig;
-import io.helidon.inject.api.Bootstrap;
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.testing.InjectionTestingSupport;
+import io.helidon.service.registry.ServiceRegistryManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.MethodOrderer;
@@ -35,22 +33,25 @@
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ConfigProducerTest {
+ private ServiceRegistryManager registryManager;
+
@AfterEach
- void reset() {
- InjectionTestingSupport.resetAll();
+ void shutdownServices() {
+ if (registryManager != null) {
+ registryManager.shutdown();
+ }
}
@Test
- @Order(0) // this must be first, as once we set global config, this method will always fail
+ @Order(0)
+ // this must be first, as once we set global config, this method will always fail
void testConfig() {
- InjectionServices.globalBootstrap(Bootstrap.builder()
- .config(GlobalConfig.config())
- .build());
+ registryManager = ServiceRegistryManager.create();
// value should be overridden using our custom config source
- Config config = InjectionServices.realizedServices()
- .lookup(Config.class)
- .get();
+ Config config = registryManager
+ .registry()
+ .get(Config.class);
assertThat(config.get("app.value").asString().asOptional(), is(Optional.of("source")));
}
@@ -61,13 +62,11 @@ void testExplicitConfig() {
// value should use the config as we provided it
GlobalConfig.config(io.helidon.config.Config::create, true);
- InjectionServices.globalBootstrap(Bootstrap.builder()
- .config(GlobalConfig.config())
- .build());
+ registryManager = ServiceRegistryManager.create();
- Config config = InjectionServices.realizedServices()
- .lookup(Config.class)
- .get();
+ Config config = registryManager
+ .registry()
+ .get(Config.class);
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
index 6769a3ceb99..a98bdab8457 100644
--- 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -22,15 +22,11 @@
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.inject.api.ExternalContracts;
+import io.helidon.service.registry.Service;
-import jakarta.inject.Singleton;
-
-@Singleton
+@Service.Provider
@Weight(200)
-@ExternalContracts(ConfigSource.class)
class TestConfigSource implements NodeConfigSource {
@Override
diff --git a/config/tests/service-registry/src/test/resources/application.yaml b/config/tests/service-registry/src/test/resources/application.yaml
index a0154957f7d..f048a795f74 100644
--- a/config/tests/service-registry/src/test/resources/application.yaml
+++ b/config/tests/service-registry/src/test/resources/application.yaml
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
@@ -15,8 +15,3 @@
#
app.value: "file"
-
-# needed to reset state of service registry in test
-inject:
- permits-dynamic: true
- service-lookup-caching: true
diff --git a/etc/checkstyle-suppressions.xml b/etc/checkstyle-suppressions.xml
index 4f3db1bae81..e3af4e8b4bf 100644
--- a/etc/checkstyle-suppressions.xml
+++ b/etc/checkstyle-suppressions.xml
@@ -1,7 +1,7 @@
+ PostConstruct and RunLevel in [Main](src/main/java/io/helidon/examples/inject/basics/Main.java).
-* annotation processing and source code generation (see [pom.xml](pom.xml) and [generated-sources](target/generated-sources/annotations/io/helidon/examples/inject/basics)).
-
-## Build and run
-
-```shell
-mvn package
-java -jar target/helidon-examples-inject-basics.jar
-```
-
-Expected Output:
-```
-Startup service providers (ranked according to weight, pre-activated): [ToolBox:INIT]
-Highest weighted service provider: ToolBox:INIT
-Preferred (highest weighted) 'Big' Tool: Big Hammer
-Optional 'Little' Hammer: Optional[Little Hammer]
-Tools in the virtual ToolBox:
- tool: Hammer:INIT
- tool: BigHammer:ACTIVE
- tool: LittleHammer:ACTIVE
-Highest weighted service provider (after activation): ToolBox
-All service providers (after all activations): [ToolBox:ACTIVE]
-```
diff --git a/examples/inject/basics/pom.xml b/examples/inject/basics/pom.xml
deleted file mode 100644
index a9edb39e601..00000000000
--- a/examples/inject/basics/pom.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.applications
- helidon-se
- 4.0.0-SNAPSHOT
- ../../../applications/se/pom.xml
-
- io.helidon.examples.inject
- helidon-examples-inject-basics
- Helidon Examples Injection Basics
-
-
- Examples of programmatic and declarative usages of Injection.
-
-
-
- io.helidon.examples.inject.basics.Main
-
-
-
-
- io.helidon.inject
- helidon-inject-api
-
-
- io.helidon.inject
- helidon-inject-runtime
-
-
- jakarta.annotation
- jakarta.annotation-api
- provided
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
- -Ainject.autoAddNonContractInterfaces=false
- -Ainject.debug=false
- -Ainject.acceptPreview=true
-
- true
-
-
- io.helidon.inject
- helidon-inject-processor
- ${helidon.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
-
-
-
-
-
-
diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/LittleHammer.java b/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/LittleHammer.java
deleted file mode 100644
index 824712912b6..00000000000
--- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/LittleHammer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.examples.inject.basics;
-
-import jakarta.inject.Singleton;
-
-@Little
-@Singleton
-class LittleHammer extends Hammer {
-
- @Override
- public String name() {
- return "Little " + super.name();
- }
-
-}
diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Main.java b/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Main.java
deleted file mode 100644
index 50007ddc5b6..00000000000
--- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Main.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.examples.inject.basics;
-
-import java.util.List;
-
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.RunLevel;
-import io.helidon.inject.api.ServiceInfoCriteria;
-import io.helidon.inject.api.ServiceProvider;
-import io.helidon.inject.api.Services;
-
-/**
- * Basics example.
- */
-public class Main {
-
- /**
- * Executes the example.
- *
- * @param args arguments
- */
- public static void main(String... args) {
- Services services = InjectionServices.realizedServices();
-
- // 0. Demonstrates programmatic lookup from the Services registry.
- // 1. when a service is being managed by a DI provider (like Helidon Injection) it should be "looked up" or injected instead of new'ed
- // 2. Notice we get a ServiceProvider - service providers allow for lazy initialization
- ServiceInfoCriteria criteria = ServiceInfoCriteria.builder()
- .runLevel(RunLevel.STARTUP)
- .build();
-
- List> startupServiceProviders = services.lookupAll(criteria);
- System.out.println("Startup service providers (ranked according to weight, pre-activated): " + startupServiceProviders);
-
- ServiceProvider> highestWeightedServiceProvider = services.lookupFirst(criteria);
- System.out.println("Highest weighted service provider: " + highestWeightedServiceProvider);
-
- // trigger lazy activations for the highest weighted service provider
- System.out.println("Highest weighted service provider (after activation): " + highestWeightedServiceProvider.get());
-
- // trigger all activations for the (remaining unactivated) startup service providers
- startupServiceProviders.forEach(ServiceProvider::get);
- System.out.println("All service providers (after all activations): " + startupServiceProviders);
- }
-
-}
diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/ToolBox.java b/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/ToolBox.java
deleted file mode 100644
index 781b5f6efa6..00000000000
--- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/ToolBox.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.examples.inject.basics;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-import io.helidon.common.Weight;
-import io.helidon.common.Weighted;
-import io.helidon.inject.api.RunLevel;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.inject.Provider;
-import jakarta.inject.Singleton;
-
-/**
- * By adding the {@link Singleton} annotation results in ToolBox becoming a service. Services can be looked up
- * programmatically or declaratively injected via {@link jakarta.inject.Inject}.
- *
- * Here {@link Weight} is used that is higher than the default, making it more preferred in weighted rankings.
- */
-@Singleton
-@RunLevel(RunLevel.STARTUP)
-@Weight(Weighted.DEFAULT_WEIGHT + 1)
-public class ToolBox {
-
- private final List> allToolProviders;
- private Tool preferredBigTool;
-
- // Field injection is supported for non-static, non-private methods (but not recommended)
- // Here we are using it to also showcase for Optional usages.
- @Inject Optional optionalLittleHammer;
-
- /**
- * Here the constructor injects all {@link Tool} provider instances available. {@link Provider} is used to allow lazy
- * activation of services until {@link Provider#get()} is called.
- *
- * @param allToolProviders all tool providers
- */
- @Inject
- ToolBox(List> allToolProviders) {
- this.allToolProviders = Objects.requireNonNull(allToolProviders);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-
- /**
- * Example of setter based injection.
- *
- * @param preferredBigTool the preferred big tool
- */
- @Inject
- @SuppressWarnings("unused")
- void setPreferredBigTool(@Big Tool preferredBigTool) {
- this.preferredBigTool = Objects.requireNonNull(preferredBigTool);
- }
-
- /**
- * This method will be called by Pico after this instance is lazily initialized (because this is the {@link PostConstruct}
- * method).
- */
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println("Preferred (highest weighted) 'Big' Tool: " + preferredBigTool);
- System.out.println("Optional 'Little' Hammer: " + optionalLittleHammer);
-
- printToolBoxContents();
- }
-
- public void printToolBoxContents() {
- System.out.println("Tools in the virtual ToolBox:");
- for (Provider tool : allToolProviders) {
- System.out.println(" tool: " + tool);
- }
- }
-
-}
diff --git a/examples/inject/basics/src/main/resources/logging.properties b/examples/inject/basics/src/main/resources/logging.properties
deleted file mode 100644
index bd06e0ed087..00000000000
--- a/examples/inject/basics/src/main/resources/logging.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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.
-#
-
-
-handlers = java.util.logging.ConsoleHandler
-
-java.util.logging.ConsoleHandler.level = FINEST
-java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
-java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n
-
-.level = INFO
-io.helidon.config.level = WARNING
-io.helidon.config.examples.level = FINEST
diff --git a/examples/inject/configdriven/README.md b/examples/inject/configdriven/README.md
deleted file mode 100644
index 00b6e219118..00000000000
--- a/examples/inject/configdriven/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Helidon Injection Config-Driven Example
-
-This example shows the basics of using Helidon Injection's Config-Driven Services. The
-[Main.java](src/main/java/io/helidon/examples/inject/configdriven/Main.java) class shows:
-
-* setting up the bootstrap [configuration](./src/main/resources/application.yaml).
-* [ConfigBean](src/main/java/io/helidon/examples/inject/configdriven/DrillConfig.java).
-* [ConfiguredBy](src/main/java/io/helidon/examples/inject/configdriven/Drill.java) Services.
-* annotation processing and source code generation (see [pom.xml](pom.xml) and [generated-sources](target/generated-sources/annotations/io/helidon/examples/inject/configdriven)).
-
-## Build and run
-
-```shell
-mvn package
-java -jar target/helidon-examples-inject-configdriven.jar
-```
-
-Expected Output:
-```
-Preferred (highest weighted) 'Big' Tool: Big Hammer
-Optional 'Little' Hammer: Optional[Little Hammer]
-Tools in the virtual ToolBox:
- tool: Hammer:INIT
- tool: BigHammer:ACTIVE
- tool: LittleHammer:ACTIVE
- tool: Drill{Hand}:PENDING
- tool: Drill{Impact}:PENDING
-```
diff --git a/examples/inject/configdriven/pom.xml b/examples/inject/configdriven/pom.xml
deleted file mode 100644
index e9fb4eeedd4..00000000000
--- a/examples/inject/configdriven/pom.xml
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.applications
- helidon-se
- 4.0.0-SNAPSHOT
- ../../../applications/se/pom.xml
-
- io.helidon.examples.inject
- helidon-examples-inject-configdriven
- Helidon Examples Injection Config-Driven
-
-
- Examples of Config-driven services in Injection.
-
-
-
- io.helidon.examples.inject.configdriven.Main
-
-
-
-
- io.helidon.examples.inject
- helidon-examples-inject-basics
- ${helidon.version}
-
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-runtime
-
-
- io.helidon.config
- helidon-config-yaml
-
-
- io.helidon.config
- helidon-config-metadata
- true
-
-
- jakarta.annotation
- jakarta.annotation-api
- provided
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- true
-
-
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
- ${helidon.version}
-
-
-
- io.helidon.builder
- helidon-builder-processor
- ${helidon.version}
-
-
-
-
-
- io.helidon.builder
- helidon-builder-processor
- ${helidon.version}
-
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
- ${helidon.version}
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
-
-
-
-
-
-
diff --git a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Drill.java b/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Drill.java
deleted file mode 100644
index 9c843790410..00000000000
--- a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Drill.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.examples.inject.configdriven;
-
-import java.util.Objects;
-
-import io.helidon.examples.inject.basics.Tool;
-import io.helidon.inject.configdriven.api.ConfigDriven;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-
-@ConfigDriven(DrillConfigBlueprint.class)
-class Drill implements Tool {
-
- private final DrillConfig cfg;
-
- @Inject
- Drill(DrillConfig cfg) {
- this.cfg = Objects.requireNonNull(cfg);
- }
-
- @Override
- public String name() {
- return cfg.name();
- }
-
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println(name() + "; initialized");
- }
-
-}
diff --git a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/DrillConfigBlueprint.java b/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/DrillConfigBlueprint.java
deleted file mode 100644
index bf782b095da..00000000000
--- a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/DrillConfigBlueprint.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.examples.inject.configdriven;
-
-import io.helidon.builder.api.Prototype;
-import io.helidon.config.metadata.Configured;
-import io.helidon.config.metadata.ConfiguredOption;
-import io.helidon.inject.configdriven.api.ConfigBean;
-
-@ConfigBean(repeatable = true)
-@Prototype.Blueprint
-@Configured(root = true, prefix = "drill")
-interface DrillConfigBlueprint {
- @ConfiguredOption
- String name();
-
-}
diff --git a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Main.java b/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Main.java
deleted file mode 100644
index c6aabe88c2d..00000000000
--- a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/Main.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.examples.inject.configdriven;
-
-import io.helidon.config.Config;
-import io.helidon.config.ConfigSources;
-import io.helidon.examples.inject.basics.ToolBox;
-import io.helidon.inject.api.Bootstrap;
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.Services;
-
-/**
- * Config-driven example.
- */
-public class Main {
-
- /**
- * Executes the example.
- *
- * @param args arguments
- */
- public static void main(String... args) {
- // we need to first initialize Injection - informing the framework where to find the application's Config
- Config config = Config.builder()
- .addSource(ConfigSources.classpath("application.yaml"))
- .disableSystemPropertiesSource()
- .disableEnvironmentVariablesSource()
- .build();
- Bootstrap bootstrap = Bootstrap.builder()
- .config(config)
- .build();
- InjectionServices.globalBootstrap(bootstrap);
-
- // this drives config-driven service activations (see the contents of the toolbox being output)
- Services services = InjectionServices.realizedServices();
-
- // this will trigger the PostConstruct method to display the contents of the toolbox
- services.lookupFirst(ToolBox.class).get();
- }
-
-}
diff --git a/examples/inject/configdriven/src/main/resources/logging.properties b/examples/inject/configdriven/src/main/resources/logging.properties
deleted file mode 100644
index bd06e0ed087..00000000000
--- a/examples/inject/configdriven/src/main/resources/logging.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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.
-#
-
-
-handlers = java.util.logging.ConsoleHandler
-
-java.util.logging.ConsoleHandler.level = FINEST
-java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
-java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n
-
-.level = INFO
-io.helidon.config.level = WARNING
-io.helidon.config.examples.level = FINEST
diff --git a/examples/inject/interceptors/README.md b/examples/inject/interceptors/README.md
deleted file mode 100644
index 9e01c210d17..00000000000
--- a/examples/inject/interceptors/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Helidon Injection Providers Example
-
-This example shows how interceptors can be leveraged to develop using Helidon Injection. The
-[Main.java](./src/main/java/io/helidon/examples/inject/providers/Main.java) class shows:
-
-* Interception basics of Injection.
-
-## Build and run
-
-```shell
-mvn package
-java -jar target/helidon-examples-inject-interceptors.jar
-```
-
-Expected Output:
-```
-Screw Driver (1st turn):
-Screw Driver turning right
-Screw Driver (2nd turn):
-Screw Driver turning right
-```
diff --git a/examples/inject/interceptors/pom.xml b/examples/inject/interceptors/pom.xml
deleted file mode 100644
index 5ab470a9d6e..00000000000
--- a/examples/inject/interceptors/pom.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.applications
- helidon-se
- 4.0.0-SNAPSHOT
- ../../../applications/se/pom.xml
-
- io.helidon.examples.inject
- helidon-examples-inject-interceptors
- Helidon Examples Injection Interceptors
-
-
- Example usages of Injection in Interceptors.
-
-
-
- io.helidon.examples.inject.interceptors.Main
-
-
-
-
- io.helidon.examples.inject
- helidon-examples-inject-basics
- ${helidon.version}
-
-
- jakarta.annotation
- jakarta.annotation-api
- provided
-
-
- org.hamcrest
- hamcrest-all
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- true
-
- -Ainject.acceptPreview=true
-
-
-
- io.helidon.inject
- helidon-inject-processor
- ${helidon.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
-
-
-
-
-
-
diff --git a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Main.java b/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Main.java
deleted file mode 100644
index 5e0ae74355c..00000000000
--- a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Main.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.examples.inject.interceptors;
-
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.ServiceProvider;
-import io.helidon.inject.api.Services;
-
-/**
- * Interceptors example.
- */
-public class Main {
-
- /**
- * Executes the example.
- *
- * @param args arguments
- */
- public static void main(String... args) {
- Services services = InjectionServices.realizedServices();
-
- // use the intercepted screwdriver - note that hashCode(), equals(), and toString() are not intercepted
- ServiceProvider screwDriver = services.lookupFirst(ScrewDriver.class);
- System.out.println(screwDriver.get() + " (1st turn): ");
- screwDriver.get().turn("left");
-
- // use the intercepted screwdriver turning tool - note that hashCode(), equals(), and toString() are not intercepted
- ServiceProvider turningTool = services.lookupFirst(TurningTool.class);
- System.out.println(turningTool.get() + " (2nd turn): ");
- turningTool.get().turn("left");
- }
-
-}
diff --git a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurnInterceptor.java b/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurnInterceptor.java
deleted file mode 100644
index 74ee823a692..00000000000
--- a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurnInterceptor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.examples.inject.interceptors;
-
-import io.helidon.inject.api.ClassNamed;
-import io.helidon.inject.api.Interceptor;
-import io.helidon.inject.api.InvocationContext;
-
-import jakarta.inject.Singleton;
-
-@ClassNamed(Turn.class)
-@Singleton
-@SuppressWarnings("unused")
-class TurnInterceptor implements Interceptor {
-
- @Override
- @SuppressWarnings("unchecked")
- public V proceed(InvocationContext ctx,
- Chain chain,
- Object... args) {
- // in "real life" you'd use the ctx to determine the best decision - this is just for simple demonstration only!
- if (args.length == 1) {
- // this is the call to turn()
- args[0] = "right";
- } else if (args.length == 0 && ctx.elementInfo().elementName().equals("name")) {
- return (V) ("intercepted: " + chain.proceed(args));
- }
-
- return chain.proceed(args);
- }
-
-}
diff --git a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurningTool.java b/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurningTool.java
deleted file mode 100644
index 128aa8ba0fc..00000000000
--- a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/TurningTool.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.examples.inject.interceptors;
-
-import io.helidon.examples.inject.basics.Tool;
-import io.helidon.inject.api.Contract;
-
-@Contract
-public interface TurningTool extends Tool {
-
- void turn(String direction);
-
-}
diff --git a/examples/inject/interceptors/src/main/resources/logging.properties b/examples/inject/interceptors/src/main/resources/logging.properties
deleted file mode 100644
index bd06e0ed087..00000000000
--- a/examples/inject/interceptors/src/main/resources/logging.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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.
-#
-
-
-handlers = java.util.logging.ConsoleHandler
-
-java.util.logging.ConsoleHandler.level = FINEST
-java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
-java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n
-
-.level = INFO
-io.helidon.config.level = WARNING
-io.helidon.config.examples.level = FINEST
diff --git a/examples/inject/pom.xml b/examples/inject/pom.xml
deleted file mode 100644
index 895fb067670..00000000000
--- a/examples/inject/pom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.examples
- helidon-examples-project
- 4.0.0-SNAPSHOT
-
- io.helidon.examples.inject
- helidon-examples-inject-project
- pom
- Helidon Examples Injection
-
-
- basics
- providers
- configdriven
- interceptors
-
-
-
diff --git a/examples/inject/providers/README.md b/examples/inject/providers/README.md
deleted file mode 100644
index 57e5fa94f94..00000000000
--- a/examples/inject/providers/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Helidon Injection Providers Example
-
-This example shows how providers can be leveraged to develop using Helidon Injection. The
-[Main.java](./src/main/java/io/helidon/examples/inject/providers/Main.java) class shows:
-
-* multi-module usage (i.e., this example extends [basics](../basics)).
-* [standard Providers](src/main/java/io/helidon/examples/inject/providers/NailProvider.java).
-* [InjectionPoint Providers](src/main/java/io/helidon/examples/inject/providers/BladeProvider.java).
-* additional lifecycle examples via PostConstruct and RunLevel.
-
-## Build and run
-
-```shell
-mvn package
-java -jar target/helidon-examples-inject-providers.jar
-```
-
-Expected Output:
-```
-Startup service providers (ranked according to weight, pre-activated): [ToolBox:INIT, CircularSaw:INIT, NailGun:INIT, TableSaw:INIT]
-Preferred (highest weighted) 'Big' Tool: Big Hammer
-Optional 'Little' Hammer: Optional[Little Hammer]
-Tools in the virtual ToolBox:
- tool: Hammer:INIT
- tool: BigHammer:ACTIVE
- tool: LittleHammer:ACTIVE
- tool: AngleGrinderSaw:INIT
- tool: CircularSaw:INIT
- tool: HandSaw:INIT
- tool: NailGun:INIT
- tool: TableSaw:INIT
-io.helidon.examples.inject.providers.CircularSaw:: will be injected with Optional.empty
-Circular Saw: (blade=null); initialized
-Nail Gun: (nail provider=NailProvider:INIT); initialized
-io.helidon.examples.inject.providers.TableSaw:: will be injected with Optional[LARGE Blade]
-Table Saw: (blade=LARGE Blade); initialized
-All service providers (after all activations): [ToolBox:ACTIVE, CircularSaw:ACTIVE, NailGun:ACTIVE, TableSaw:ACTIVE]
-```
diff --git a/examples/inject/providers/pom.xml b/examples/inject/providers/pom.xml
deleted file mode 100644
index 29ec907114e..00000000000
--- a/examples/inject/providers/pom.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.applications
- helidon-se
- 4.0.0-SNAPSHOT
- ../../../applications/se/pom.xml
-
- io.helidon.examples.inject
- helidon-examples-inject-providers
- Helidon Examples Injection Providers
-
-
- Example usages of Injection Providers.
-
-
-
- io.helidon.examples.inject.providers.Main
-
-
-
-
- io.helidon.examples.inject
- helidon-examples-inject-basics
- ${helidon.version}
-
-
- jakarta.annotation
- jakarta.annotation-api
- provided
-
-
- org.hamcrest
- hamcrest-all
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- true
-
- -Ainject.autoAddNonContractInterfaces=true
- -Ainject.debug=false
- -Ainject.acceptPreview=true
-
-
-
- io.helidon.inject
- helidon-inject-processor
- ${helidon.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
-
-
-
-
-
-
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/AngleGrinderSaw.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/AngleGrinderSaw.java
deleted file mode 100644
index ea9565c2caf..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/AngleGrinderSaw.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.Optional;
-
-import io.helidon.examples.inject.basics.Little;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-class AngleGrinderSaw implements Saw {
-
- private final Blade blade;
-
- @Inject
- AngleGrinderSaw(@Little Optional blade) {
- this.blade = blade.orElse(null);
- }
-
- @Override
- public String name() {
- return "Angle Grinder Saw: (blade=" + blade + ")";
- }
-
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println(name() + "; initialized");
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/BladeProvider.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/BladeProvider.java
deleted file mode 100644
index 759cdcd1e36..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/BladeProvider.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.Optional;
-
-import io.helidon.common.LazyValue;
-import io.helidon.examples.inject.basics.Big;
-import io.helidon.examples.inject.basics.Little;
-import io.helidon.inject.api.ContextualServiceQuery;
-import io.helidon.inject.api.InjectionPointInfo;
-import io.helidon.inject.api.InjectionPointProvider;
-import io.helidon.inject.api.Qualifier;
-import io.helidon.inject.api.ServiceInfoCriteria;
-
-import jakarta.inject.Singleton;
-
-import static io.helidon.common.LazyValue.create;
-
-@Singleton
-public class BladeProvider implements InjectionPointProvider {
-
- static final LazyValue> LARGE_BLADE = create(() -> Optional.of(new SizedBlade(SizedBlade.Size.LARGE)));
- static final LazyValue> SMALL_BLADE = create(() -> Optional.of(new SizedBlade(SizedBlade.Size.SMALL)));
-
- /**
- * Here we are creating the right sized blade based upon the injection point's criteria. Note that the scope/cardinality
- * is still (0..1), meaning there will be at most 1 LARGE and at most 1 SMALL blades provided.
- * All {@code Provider}s control the scope of the service instances they provide.
- *
- * @param query the service query
- * @return the blade appropriate for the injection point, or empty if nothing matches
- *
- * @see NailProvider
- */
- @Override
- public Optional first(ContextualServiceQuery query) {
- ServiceInfoCriteria criteria = query.serviceInfoCriteria();
- if (contains(criteria.qualifiers(), Big.class)) {
- return logAndReturn(LARGE_BLADE.get(), query);
- } else if (contains(criteria.qualifiers(), Little.class)) {
- return logAndReturn(SMALL_BLADE.get(), query);
- }
- return logAndReturn(Optional.empty(), query);
- }
-
- static Optional logAndReturn(Optional result,
- ContextualServiceQuery query) {
- InjectionPointInfo ip = query.injectionPointInfo().orElse(null);
- // note: a "regular" service lookup via Injection will not have an injection point associated with it
- if (ip != null) {
- System.out.println(ip.serviceTypeName() + "::" + ip.elementName() + " will be injected with " + result);
- }
- return result;
- }
-
- static boolean contains(Collection qualifiers,
- Class extends Annotation> anno) {
- return qualifiers.stream().anyMatch(it -> it.typeName().name().equals(anno.getName()));
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/CircularSaw.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/CircularSaw.java
deleted file mode 100644
index cb951417b6a..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/CircularSaw.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.Optional;
-
-import io.helidon.inject.api.RunLevel;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@RunLevel(RunLevel.STARTUP)
-class CircularSaw implements Saw {
-
- private final Blade blade;
-
- @Inject
- CircularSaw(Optional blade) {
- this.blade = blade.orElse(null);
- }
-
- @Override
- public String name() {
- return "Circular Saw: (blade=" + blade + ")";
- }
-
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println(name() + "; initialized");
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/HandSaw.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/HandSaw.java
deleted file mode 100644
index 1a9961ec302..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/HandSaw.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.Optional;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-import jakarta.inject.Singleton;
-
-@Singleton
-class HandSaw implements Saw {
-
- private final Blade blade;
-
- @Inject
- HandSaw(@Named("replacement-blade-that-does-not-exist") Optional blade) {
- this.blade = blade.orElse(null);
- }
-
- @Override
- public String name() {
- return "Hand Saw: (blade=" + blade + ")";
- }
-
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println(name() + "; initialized");
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Main.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Main.java
deleted file mode 100644
index 9c706922311..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Main.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.List;
-
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.RunLevel;
-import io.helidon.inject.api.ServiceInfoCriteria;
-import io.helidon.inject.api.ServiceProvider;
-import io.helidon.inject.api.Services;
-
-/**
- * Providers example.
- */
-public class Main {
-
- /**
- * Executes the example.
- *
- * @param args arguments
- */
- public static void main(String... args) {
- Services services = InjectionServices.realizedServices();
-
- ServiceInfoCriteria criteria = ServiceInfoCriteria.builder()
- .runLevel(RunLevel.STARTUP)
- .build();
-
- List> startupServiceProviders = services.lookupAll(criteria);
- System.out.println("Startup service providers (ranked according to weight, pre-activated): " + startupServiceProviders);
-
- // trigger all activations for startup service providers
- startupServiceProviders.forEach(ServiceProvider::get);
- System.out.println("All service providers (after all activations): " + startupServiceProviders);
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Nail.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Nail.java
deleted file mode 100644
index b14c5f5837f..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Nail.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import io.helidon.inject.api.Contract;
-import io.helidon.inject.api.Services;
-
-/**
- * Normally, one would need to place {@link Contract} on interfaces. Here, however, we used
- * {@code -Ainject.autoAddNonContractInterfaces=true} in the {@code pom.xml} thereby making all interfaces into contracts that
- * can be found via {@link Services#lookup}.
- */
-//@Contract
-public interface Nail {
-
- int id();
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailGun.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailGun.java
deleted file mode 100644
index f19c713d616..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailGun.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.Objects;
-
-import io.helidon.examples.inject.basics.Tool;
-import io.helidon.inject.api.RunLevel;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.inject.Provider;
-import jakarta.inject.Singleton;
-
-@Singleton
-@RunLevel(RunLevel.STARTUP)
-class NailGun implements Tool {
-
- private final Provider nailProvider;
-
- @Inject
- NailGun(Provider nailProvider) {
- this.nailProvider = Objects.requireNonNull(nailProvider);
- }
-
- @Override
- public String name() {
- return "Nail Gun: (nail provider=" + nailProvider + ")";
- }
-
- /**
- * This method will be called by Injection after this instance is lazily initialized (because this is the {@link PostConstruct}
- * method).
- */
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println(name() + "; initialized");
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailProvider.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailProvider.java
deleted file mode 100644
index 1d2bd7bfe6b..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/NailProvider.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import jakarta.inject.Provider;
-import jakarta.inject.Singleton;
-
-/**
- * Showcases dependent scope-creating a nail for caller's demand for a {@link Nail} to be provided.
- * All {@code Provider}s control the scope of the service instances they provide.
- *
- * @see BladeProvider
- */
-@Singleton
-class NailProvider implements Provider {
-
- /**
- * Creates a new nail every its called.
- *
- * @return a new nail instance
- */
- @Override
- public Nail get() {
- return new StandardNail();
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Saw.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Saw.java
deleted file mode 100644
index cb73214ea37..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Saw.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import io.helidon.examples.inject.basics.Tool;
-import io.helidon.inject.api.Contract;
-import io.helidon.inject.api.Services;
-
-/**
- * Normally, one would need to place {@link Contract} on interfaces. Here, however, we used
- * {@code -Ainject.autoAddNonContractInterfaces=true} in the {@code pom.xml} thereby making all interfaces into contracts that
- * can be found via {@link Services#lookup}.
- */
-//@Contract
-public interface Saw extends Tool {
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/SizedBlade.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/SizedBlade.java
deleted file mode 100644
index fdb830560b7..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/SizedBlade.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.Objects;
-
-/**
- * See {@link Blade}
- */
-class SizedBlade implements Blade {
-
- private final Size size;
-
- public enum Size {
- SMALL,
- LARGE
- }
-
- public SizedBlade(Size size) {
- this.size = Objects.requireNonNull(size);
- }
-
- @Override
- public String name() {
- return size + " Blade";
- }
-
- @Override
- public String toString() {
- return name();
- }
-
-}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/TableSaw.java b/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/TableSaw.java
deleted file mode 100644
index 70f9bd2b9a5..00000000000
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/TableSaw.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import java.util.Optional;
-
-import io.helidon.examples.inject.basics.Big;
-import io.helidon.inject.api.RunLevel;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-
-@Singleton
-@RunLevel(RunLevel.STARTUP)
-class TableSaw implements Saw {
-
- private final Blade blade;
-
- @Inject
- TableSaw(@Big Optional blade) {
- this.blade = blade.orElse(null);
- }
-
- @Override
- public String name() {
- return "Table Saw: (blade=" + blade + ")";
- }
-
- @PostConstruct
- @SuppressWarnings("unused")
- void init() {
- System.out.println(name() + "; initialized");
- }
-
-}
diff --git a/examples/inject/providers/src/main/resources/logging.properties b/examples/inject/providers/src/main/resources/logging.properties
deleted file mode 100644
index bd06e0ed087..00000000000
--- a/examples/inject/providers/src/main/resources/logging.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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.
-#
-
-
-handlers = java.util.logging.ConsoleHandler
-
-java.util.logging.ConsoleHandler.level = FINEST
-java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
-java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n
-
-.level = INFO
-io.helidon.config.level = WARNING
-io.helidon.config.examples.level = FINEST
diff --git a/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/ProvidersTest.java b/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/ProvidersTest.java
deleted file mode 100644
index 105f0e86bf0..00000000000
--- a/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/ProvidersTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.examples.inject.providers;
-
-import io.helidon.examples.inject.basics.ToolBox;
-
-import org.junit.jupiter.api.Test;
-
-class ProvidersTest {
-
- /**
- * Through testing, this will additionally have an {@link AllenWrench} in the {@link ToolBox}.
- */
- @Test
- void main() {
- Main.main();
- }
-
-}
diff --git a/examples/pom.xml b/examples/pom.xml
index b8fe6d01211..ac4c94ea6f6 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -47,7 +47,6 @@
graphqlhealthintegrations
- injectjbatchloggingmedia
diff --git a/fault-tolerance/fault-tolerance/pom.xml b/fault-tolerance/fault-tolerance/pom.xml
index e86c5474f24..4e51b161d8e 100644
--- a/fault-tolerance/fault-tolerance/pom.xml
+++ b/fault-tolerance/fault-tolerance/pom.xml
@@ -41,23 +41,6 @@
io.helidon.commonhelidon-common-configurable
-
-
- io.helidon.inject
- helidon-inject-api
-
-
-
- io.helidon.inject
- helidon-inject-runtime
- true
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-api
- true
-
-
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-runtime
- true
- org.hamcresthamcrest-all
@@ -129,11 +95,6 @@
helidon-config-metadata-processor${helidon.version}
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
- ${helidon.version}
- io.helidon.codegenhelidon-codegen-apt
@@ -157,11 +118,6 @@
helidon-config-metadata-processor${helidon.version}
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
- ${helidon.version}
- io.helidon.common.featureshelidon-common-features-processor
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Async.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Async.java
index b3918dd9ee9..84188d97fcb 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Async.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Async.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 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.
@@ -21,13 +21,11 @@
import java.util.function.Supplier;
import io.helidon.builder.api.RuntimeType;
-import io.helidon.inject.api.Contract;
/**
* Runs synchronous suppliers asynchronously using virtual threads. Includes
* convenient static method to avoid creating instances of this class.
*/
-@Contract
@RuntimeType.PrototypedBy(AsyncConfig.class)
public interface Async extends RuntimeType.Api {
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/AsyncImpl.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/AsyncImpl.java
index 22edfa41581..fffab873415 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/AsyncImpl.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/AsyncImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 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.
@@ -16,20 +16,12 @@
package io.helidon.faulttolerance;
-import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.Qualifier;
-import io.helidon.inject.api.ServiceInfoCriteria;
-import io.helidon.inject.api.ServiceProvider;
-
-import jakarta.inject.Inject;
-
/**
* Implementation of {@code Async}. Default executor accessed from {@link FaultTolerance#executor()}.
*/
@@ -40,16 +32,6 @@ class AsyncImpl implements Async {
private final CompletableFuture onStart;
private final AsyncConfig config;
- // this must only be invoked when within Pico, so we can use pico services
- @Inject
- AsyncImpl(AsyncConfig config) {
- this.executor = config.executor()
- .or(() -> config.executorName().flatMap(AsyncImpl::executorService))
- .orElseGet(() -> FaultTolerance.executor().get());
- this.onStart = config.onStart().orElseGet(CompletableFuture::new);
- this.config = config;
- }
-
AsyncImpl(AsyncConfig config, boolean internal) {
this.executor = config.executor().orElseGet(() -> FaultTolerance.executor().get());
this.onStart = config.onStart().orElseGet(CompletableFuture::new);
@@ -96,14 +78,4 @@ public boolean cancel(boolean mayInterruptIfRunning) {
return result;
}
-
- private static Optional executorService(String name) {
- var qualifier = Qualifier.createNamed(name);
- return InjectionServices.realizedServices().lookupFirst(ExecutorService.class,
- ServiceInfoCriteria.builder()
- .addQualifier(qualifier)
- .build(),
- false)
- .map(ServiceProvider::get);
- }
}
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Bulkhead.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Bulkhead.java
index d0531dd8b13..98c8ac521e0 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Bulkhead.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Bulkhead.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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,7 +20,6 @@
import java.util.function.Supplier;
import io.helidon.builder.api.RuntimeType;
-import io.helidon.inject.api.Contract;
/**
* Bulkhead protects a resource that cannot serve unlimited parallel
@@ -31,7 +30,6 @@
* additional attempts to invoke will end with a failed response with
* {@link BulkheadException}.
*/
-@Contract
@RuntimeType.PrototypedBy(BulkheadConfig.class)
public interface Bulkhead extends FtHandler, RuntimeType.Api {
/**
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadConfigBlueprint.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadConfigBlueprint.java
index 2e31bdaf7f8..7e1d62847c9 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadConfigBlueprint.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadConfigBlueprint.java
@@ -21,12 +21,10 @@
import io.helidon.builder.api.Option;
import io.helidon.builder.api.Prototype;
-import io.helidon.inject.configdriven.api.ConfigBean;
/**
* {@link Bulkhead} configuration bean.
*/
-@ConfigBean(repeatable = true)
@Prototype.Configured("fault-tolerance.bulkheads")
@Prototype.Blueprint(decorator = BulkheadConfigBlueprint.BuilderDecorator.class)
interface BulkheadConfigBlueprint extends Prototype.Factory {
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadImpl.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadImpl.java
index 394734f0d02..50d82096a6a 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadImpl.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/BulkheadImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -32,8 +32,6 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
-import jakarta.inject.Inject;
-
class BulkheadImpl implements Bulkhead {
private static final System.Logger LOGGER = System.getLogger(BulkheadImpl.class.getName());
@@ -48,7 +46,6 @@ class BulkheadImpl implements Bulkhead {
private final Set> cancelledSuppliers = new CopyOnWriteArraySet<>();
private final BulkheadConfig config;
- @Inject
BulkheadImpl(BulkheadConfig config) {
this.inProgress = new Semaphore(config.limit(), true);
this.name = config.name().orElseGet(() -> "bulkhead-" + System.identityHashCode(config));
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreaker.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreaker.java
index c1b7e62c64b..07004da88a5 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreaker.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreaker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -19,7 +19,6 @@
import java.util.function.Consumer;
import io.helidon.builder.api.RuntimeType;
-import io.helidon.inject.api.Contract;
/**
* CircuitBreaker protects a potentially failing endpoint from overloading and the application
@@ -31,7 +30,6 @@
* and requests can process as usual again.
*/
@RuntimeType.PrototypedBy(CircuitBreakerConfig.class)
-@Contract
public interface CircuitBreaker extends FtHandler, RuntimeType.Api {
/**
* Create a new circuit builder based on its configuration.
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerConfigBlueprint.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerConfigBlueprint.java
index 72d22facdf3..1d59f607d45 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerConfigBlueprint.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerConfigBlueprint.java
@@ -23,11 +23,9 @@
import io.helidon.builder.api.Option;
import io.helidon.builder.api.Prototype;
-import io.helidon.inject.configdriven.api.ConfigBean;
@Prototype.Blueprint(decorator = CircuitBreakerConfigBlueprint.BuilderDecorator.class)
@Prototype.Configured("fault-tolerance.circuit-breakers")
-@ConfigBean(wantDefault = true, repeatable = true)
interface CircuitBreakerConfigBlueprint extends Prototype.Factory {
int DEFAULT_ERROR_RATIO = 60;
int DEFAULT_SUCCESS_THRESHOLD = 1;
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerImpl.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerImpl.java
index 6f1b1a5d714..f5bad10bc16 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerImpl.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/CircuitBreakerImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -23,11 +23,6 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
-import io.helidon.inject.configdriven.api.ConfigDriven;
-
-import jakarta.inject.Inject;
-
-@ConfigDriven(CircuitBreakerConfigBlueprint.class)
class CircuitBreakerImpl implements CircuitBreaker {
/*
Configuration options
@@ -52,7 +47,6 @@ class CircuitBreakerImpl implements CircuitBreaker {
private final String name;
private final CircuitBreakerConfig config;
- @Inject
CircuitBreakerImpl(CircuitBreakerConfig config) {
this.delayMillis = config.delay().toMillis();
this.successThreshold = config.successThreshold();
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java
index 3bd5111497c..70f78ad5e0f 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Retry.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -23,12 +23,10 @@
import java.util.function.Supplier;
import io.helidon.builder.api.RuntimeType;
-import io.helidon.inject.api.Contract;
/**
* Retry supports retry policies to be applied on an execution of asynchronous tasks.
*/
-@Contract
@RuntimeType.PrototypedBy(RetryConfig.class)
public interface Retry extends FtHandler, RuntimeType.Api {
/**
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/RetryImpl.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/RetryImpl.java
index e1145084243..a6e8c41dfa4 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/RetryImpl.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/RetryImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -25,8 +25,6 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
-import jakarta.inject.Inject;
-
class RetryImpl implements Retry {
private final ErrorChecker errorChecker;
private final long maxTimeNanos;
@@ -35,7 +33,6 @@ class RetryImpl implements Retry {
private final AtomicLong retryCounter = new AtomicLong(0L);
private final String name;
- @Inject
RetryImpl(RetryConfig retryConfig) {
this.name = retryConfig.name().orElseGet(() -> "retry-" + System.identityHashCode(retryConfig));
this.errorChecker = ErrorChecker.create(retryConfig.skipOn(), retryConfig.applyOn());
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Timeout.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Timeout.java
index f71cf303e1c..008819e826f 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Timeout.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/Timeout.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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,12 +20,10 @@
import java.util.function.Consumer;
import io.helidon.builder.api.RuntimeType;
-import io.helidon.inject.api.Contract;
/**
* Timeout attempts to terminate execution after defined duration of time.
*/
-@Contract
@RuntimeType.PrototypedBy(TimeoutConfig.class)
public interface Timeout extends FtHandler, RuntimeType.Api {
/**
diff --git a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/TimeoutImpl.java b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/TimeoutImpl.java
index fe784a2cea2..c519bc3431e 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/TimeoutImpl.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/io/helidon/faulttolerance/TimeoutImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -23,8 +23,6 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
-import jakarta.inject.Inject;
-
class TimeoutImpl implements Timeout {
private static final System.Logger LOGGER = System.getLogger(TimeoutImpl.class.getName());
@@ -34,7 +32,6 @@ class TimeoutImpl implements Timeout {
private final String name;
private final TimeoutConfig config;
- @Inject
TimeoutImpl(TimeoutConfig config) {
this.timeoutMillis = config.timeout().toMillis();
this.executor = config.executor().orElseGet(FaultTolerance.executor());
diff --git a/fault-tolerance/fault-tolerance/src/main/java/module-info.java b/fault-tolerance/fault-tolerance/src/main/java/module-info.java
index 28726cffa06..cc6869ef511 100644
--- a/fault-tolerance/fault-tolerance/src/main/java/module-info.java
+++ b/fault-tolerance/fault-tolerance/src/main/java/module-info.java
@@ -28,23 +28,11 @@
module io.helidon.faulttolerance {
requires io.helidon.common;
- requires io.helidon.common.types;
requires io.helidon.common.configurable;
requires io.helidon.config;
- requires io.helidon.inject.api;
requires io.helidon.builder.api;
- requires static jakarta.inject;
requires static io.helidon.common.features.api;
- requires static io.helidon.inject.configdriven.api;
- // needed to compile generated types
- requires static io.helidon.inject.configdriven.runtime;
- requires static io.helidon.inject.runtime;
-
exports io.helidon.faulttolerance;
-
- // inject module
- provides io.helidon.inject.api.ModuleComponent with io.helidon.faulttolerance.Injection$$Module;
-
}
diff --git a/inject/tests/resources-inject/src/test/java/io/helidon/inject/tests/inject/tbox/ToolBoxTest.java b/inject/tests/resources-inject/src/test/java/io/helidon/inject/tests/inject/tbox/ToolBoxTest.java
index a24b8a7b73b..81e4448d86b 100644
--- a/inject/tests/resources-inject/src/test/java/io/helidon/inject/tests/inject/tbox/ToolBoxTest.java
+++ b/inject/tests/resources-inject/src/test/java/io/helidon/inject/tests/inject/tbox/ToolBoxTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -180,14 +180,13 @@ 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 module as active as well
assertThat("ensure that Annotation Processors are enabled in the tools module meta-inf/services",
- desc, contains("Injection$$Module:ACTIVE", "Injection$$Module:ACTIVE", "Injection$$TestModule:ACTIVE"));
+ desc, contains("Injection$$Module:ACTIVE", "Injection$$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.config", "io.helidon.inject.tests.inject", "io.helidon.inject.tests.inject/test"));
+ contains("io.helidon.inject.tests.inject", "io.helidon.inject.tests.inject/test"));
}
/**
@@ -304,8 +303,7 @@ void startupAndShutdownCallsPostConstructAndPreDestroy() {
assertThat(report, hasEntry(create("io.helidon.inject.tests.inject.stacking.OuterCommonContractImpl"), "ACTIVE->DESTROYED"));
assertThat(report, hasEntry(create("io.helidon.inject.tests.inject.stacking.CommonContractImpl"), "ACTIVE->DESTROYED"));
assertThat(report, hasEntry(create("io.helidon.inject.tests.inject.TestingSingleton"), "ACTIVE->DESTROYED"));
- // ConfigProducer is the 9th
- assertThat(report + " : expected 9 services to be present", report.size(), equalTo(9));
+ assertThat(report + " : expected 8 services to be present", report.size(), equalTo(8));
assertThat(TestingSingleton.postConstructCount(), equalTo(1));
assertThat(TestingSingleton.preDestroyCount(), equalTo(1));
@@ -322,8 +320,7 @@ void startupAndShutdownCallsPostConstructAndPreDestroy() {
.collect(Collectors.toMap(Map.Entry::getKey,
e2 -> e2.getValue().startingActivationPhase().toString()
+ "->" + e2.getValue().finishingActivationPhase()));
- // now contains config as well
- assertThat(report.toString(), report.size(), is(9));
+ assertThat(report.toString(), report.size(), is(8));
tearDown();
map = injectionServices.shutdown().orElseThrow();
diff --git a/inject/tests/runtime/src/test/java/io/helidon/inject/runtime/HelloInjectionWorldSanityTest.java b/inject/tests/runtime/src/test/java/io/helidon/inject/runtime/HelloInjectionWorldSanityTest.java
index 0661fa3c8db..fbe7f4a90c3 100644
--- a/inject/tests/runtime/src/test/java/io/helidon/inject/runtime/HelloInjectionWorldSanityTest.java
+++ b/inject/tests/runtime/src/test/java/io/helidon/inject/runtime/HelloInjectionWorldSanityTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -67,8 +67,7 @@
* Sanity type tests only. The "real" testing is in the tests submodules.
*/
class HelloInjectionWorldSanityTest {
- // helidon-config is now one of the modules
- private static final int EXPECTED_MODULES = 3;
+ private static final int EXPECTED_MODULES = 2;
@BeforeEach
void setUp() {
@@ -96,9 +95,8 @@ void sanity() {
assertThat(moduleProviders.size(),
equalTo(EXPECTED_MODULES));
List descriptions = ServiceUtils.toDescriptions(moduleProviders);
- // helidon-config is now first
assertThat(descriptions,
- containsInAnyOrder("Injection$$Module:ACTIVE", "EmptyModule:ACTIVE", "HelloInjection$$Module:ACTIVE"));
+ containsInAnyOrder("EmptyModule:ACTIVE", "HelloInjection$$Module:ACTIVE"));
List> applications = services.lookupAll(Application.class);
assertThat(applications.size(),
diff --git a/integrations/graal/native-image-extension/pom.xml b/integrations/graal/native-image-extension/pom.xml
index 5885534f48f..b2a94d48320 100644
--- a/integrations/graal/native-image-extension/pom.xml
+++ b/integrations/graal/native-image-extension/pom.xml
@@ -49,6 +49,10 @@
io.helidon.common.featureshelidon-common-features
+
+ io.helidon.service
+ helidon-service-registry
+ org.eclipse.parssonparsson
diff --git a/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/HelidonReflectionFeature.java b/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/HelidonReflectionFeature.java
index 74309f99ffb..eb95dd04bf5 100644
--- a/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/HelidonReflectionFeature.java
+++ b/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/HelidonReflectionFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2024 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.
@@ -33,7 +33,12 @@
import io.helidon.common.Reflected;
import io.helidon.common.features.HelidonFeatures;
+import io.helidon.common.types.TypeName;
import io.helidon.logging.common.LogConfig;
+import io.helidon.service.registry.DescriptorMetadata;
+import io.helidon.service.registry.ServiceDiscovery;
+import io.helidon.service.registry.ServiceLoader__ServiceDescriptor;
+import io.helidon.service.registry.ServiceRegistryConfig;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
@@ -51,6 +56,7 @@ public class HelidonReflectionFeature implements Feature {
private static final String AT_ENTITY = "jakarta.persistence.Entity";
private static final String AT_MAPPED_SUPERCLASS = "jakarta.persistence.MappedSuperclass";
+ private static final String REGISTRY_DESCRIPTOR = "io.helidon.service.registry.GeneratedService$Descriptor";
private final NativeTrace tracer = new NativeTrace();
private NativeUtil util;
@@ -109,6 +115,9 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
// process each configured class
config.classes().forEach(it -> addSingleClass(context, it));
+ // Service descriptors
+ processServiceDescriptors(context);
+
// JPA Entity registration
processEntity(context);
@@ -173,6 +182,65 @@ private void processSubClasses(BeforeAnalysisContext context, Class> aClass) {
processClasses(context, subclasses);
}
+ private void processServiceDescriptors(BeforeAnalysisContext context) {
+ ServiceDiscovery sd = ServiceDiscovery.create(ServiceRegistryConfig.builder()
+ .discoverServices(false)
+ .discoverServicesFromServiceLoader(true)
+ .build());
+
+ sd.allMetadata()
+ .stream()
+ .map(DescriptorMetadata::descriptor)
+ .filter(it -> it instanceof ServiceLoader__ServiceDescriptor)
+ .map(it -> (ServiceLoader__ServiceDescriptor) it)
+ .map(ServiceLoader__ServiceDescriptor::serviceType)
+ .forEach(it -> processServiceLoaderDescriptor(context, it));
+
+ Class> classByName = context.access().findClassByName(REGISTRY_DESCRIPTOR);
+ tracer.parsing(() -> "Discovering service descriptors. Top level type: " + classByName);
+ if (classByName != null) {
+ processServiceDescriptors(context, classByName);
+ }
+ }
+
+ private void processServiceLoaderDescriptor(BeforeAnalysisContext context, TypeName serviceImpl) {
+ Class> classByName = context.access().findClassByName(serviceImpl.fqName());
+ if (classByName == null) {
+ tracer.parsing(() -> " " + serviceImpl.fqName());
+ tracer.parsing(() -> " service implementation is not on classpath");
+ return;
+ }
+ tracer.parsing(() -> " " + classByName.getName());
+ tracer.parsing(() -> " Added for registration");
+
+ try {
+ Constructor> constructor = classByName.getConstructor();
+ context.register(classByName).add(constructor);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Cannot find default constructor for provider implementation class " + classByName,
+ e);
+ }
+ }
+
+ private void processServiceDescriptors(BeforeAnalysisContext context, Class> clazz) {
+ Set> subclasses = util.findSubclasses(clazz.getName());
+
+ for (Class> aClass : subclasses) {
+ if (context.process(aClass)) {
+ tracer.parsing(() -> " " + aClass.getName());
+ tracer.parsing(() -> " Added for registration");
+
+ try {
+ Field field = aClass.getDeclaredField("INSTANCE");
+ context.register(aClass).add(field);
+ } catch (NoSuchFieldException ignored) {
+ // do not register, as this is not accessible via reflection
+ }
+ processServiceDescriptors(context, aClass);
+ }
+ }
+ }
+
private void addSingleClass(BeforeAnalysisContext context,
Class> theClass) {
if (context.process(theClass)) {
@@ -241,8 +309,6 @@ private void processEntity(BeforeAnalysisContext context) {
});
}
-
-
private void registerForReflection(BeforeAnalysisContext context) {
Collection toRegister = context.toRegister();
diff --git a/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/NativeUtil.java b/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/NativeUtil.java
index 0028429b640..6c67e80b602 100644
--- a/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/NativeUtil.java
+++ b/integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/NativeUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2021, 2024 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.
@@ -16,9 +16,11 @@
package io.helidon.integrations.graal.nativeimage.extension;
+import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -39,6 +41,8 @@
import io.github.classgraph.FieldInfo;
import io.github.classgraph.MethodParameterInfo;
import io.github.classgraph.ReferenceTypeSignature;
+import io.github.classgraph.Resource;
+import io.github.classgraph.ResourceList;
import io.github.classgraph.ScanResult;
import io.github.classgraph.TypeArgument;
import io.github.classgraph.TypeSignature;
@@ -265,6 +269,21 @@ public Set> findInterfaces(Class> aClass) {
return result;
}
+ List findResources(String name) {
+ ResourceList allResources = scan.getResourcesWithPath(name);
+ List list = new ArrayList<>();
+ for (Resource resource : allResources) {
+ String contentAsString = null;
+ try {
+ contentAsString = resource.getContentAsString();
+ } catch (IOException e) {
+ tracer.parsing(() -> "Failed to load resource " + resource.getPath(), e);
+ }
+ list.add(contentAsString);
+ }
+ return list;
+ }
+
void processAnnotatedFields(String annotation, BiConsumer, Field> fieldProcessor) {
InclusionFilter inclusionFilter = new InclusionFilter(tracer, exclusion, "field annotated by " + annotation);
ClassResolverMapper mapper = new ClassResolverMapper(tracer, classResolver, "field annotated by " + annotation);
diff --git a/integrations/graal/native-image-extension/src/main/java/module-info.java b/integrations/graal/native-image-extension/src/main/java/module-info.java
index 31e610fa891..8e841236606 100644
--- a/integrations/graal/native-image-extension/src/main/java/module-info.java
+++ b/integrations/graal/native-image-extension/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -23,6 +23,7 @@
requires io.helidon.common.features;
requires io.helidon.config;
requires io.helidon.logging.common;
+ requires io.helidon.service.registry;
requires jakarta.json;
requires transitive org.graalvm.nativeimage;
diff --git a/integrations/oci/README.md b/integrations/oci/README.md
new file mode 100644
index 00000000000..f418bbb88cd
--- /dev/null
+++ b/integrations/oci/README.md
@@ -0,0 +1,4 @@
+# OCI Integration
+
+Modules for integrating Oracle Cloud Infrastructure features.
+
diff --git a/integrations/oci/oci/README.md b/integrations/oci/oci/README.md
new file mode 100644
index 00000000000..21e5204a2ca
--- /dev/null
+++ b/integrations/oci/oci/README.md
@@ -0,0 +1,43 @@
+# OCI integration module
+
+This Helidon module requires Service Registry.
+
+The module uses (internally) a service of type `OciConfig`. This instance is used to configure OCI integration.
+Note that this service can be customized, if a provider with higher weight is created.
+The default service implementation uses environment variables, system properties, and a configuration file `oci-config.yaml` on file system, or on classpath (Weight: default - 10).
+
+This module provides two services that can be used by other modules.
+
+## Authentication Details Provider
+
+Any service can have a dependency on `com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider`, and it can be
+looked up using Service Registry lookup methods.
+
+The provider is looked up by using instances of `io.helidon.integrations.oci.spi.OciAtnStrategy`. The first service instance to provide an authentication provider is used, and no other service instance is called.
+
+The following out-of-the-box implementations exist:
+
+- Config based provider: uses `OciConfig` to create authentication provider; Weight: default - 10
+- Config file based provider: uses OCI config file (i.e. `~/.oci/config`) to create authentication provider; both file location and profile can be configured through `OciConfig`, Weight: default - 20
+- Resource principal provider: uses resource principal authentication provider; Weight: default - 30
+- Instance principal provider: uses instance principal authentication provider; Weight: default - 40
+
+To create a custom instance of authentication details provider, just create a new service for service registry
+with default or higher weight that provides an instance of the `AbstractAuthenticationDetailsProvider`
+(ServiceRegistry requires setup of annotation processors, see this module's pom file).
+
+## Region
+
+Any service can have a dependency on `com.oracle.bmc.Region`, and it can be looked up using Service Registry
+lookup methods.
+
+Region is discovered by using instances of `io.helidon.integrations.oci.spi.OciRegion`. The first service instance to provide a
+region is used, and no other service instance is called.
+
+The following out-of-the-box implementations exists:
+
+- Config based region provider: uses `OciConfig` to find region (expected key is `oci.region`); Weight: default - 10
+- Authentication provider based region provider: uses authentication provider if it implements `RegionProvider` to find region; Weight: default - 20
+- OCI SDK based region provider: uses `Region.registerFromInstanceMetadataService()` to find region (this has timeout of 30
+ seconds, so if we reach this provider, it may block for a while - but only once); Weight: default - 100
+
diff --git a/integrations/oci/oci/pom.xml b/integrations/oci/oci/pom.xml
new file mode 100644
index 00000000000..2ab93efb089
--- /dev/null
+++ b/integrations/oci/oci/pom.xml
@@ -0,0 +1,141 @@
+
+
+
+ 4.0.0
+
+ io.helidon.integrations.oci
+ helidon-integrations-oci-project
+ 4.0.0-SNAPSHOT
+
+ helidon-integrations-oci
+ Helidon Integrations OCI
+
+
+ Common tools for OCI integration.
+
+
+
+
+ io.helidon.service
+ helidon-service-registry
+
+
+ io.helidon.common
+ helidon-common-configurable
+
+
+ io.helidon.config
+ helidon-config
+
+
+ com.oracle.oci.sdk
+ oci-java-sdk-common
+
+
+ io.helidon.config
+ helidon-config-yaml
+ test
+
+
+ io.helidon.common.testing
+ helidon-common-testing-junit5
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.slf4j
+ slf4j-jdk14
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+
+
+ io.helidon.builder
+ helidon-builder-codegen
+ ${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-codegen
+ ${helidon.version}
+
+
+ io.helidon.codegen
+ helidon-codegen-helidon-copyright
+ ${helidon.version}
+
+
+ io.helidon.config
+ helidon-config-metadata-processor
+ ${helidon.version}
+
+
+
+
+
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+
+
+ io.helidon.builder
+ helidon-builder-codegen
+ ${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-codegen
+ ${helidon.version}
+
+
+ io.helidon.codegen
+ helidon-codegen-helidon-copyright
+ ${helidon.version}
+
+
+ io.helidon.config
+ helidon-config-metadata-processor
+ ${helidon.version}
+
+
+
+
+
+
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnDetailsProvider.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnDetailsProvider.java
new file mode 100644
index 00000000000..9a870d6383c
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnDetailsProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.config.ConfigException;
+import io.helidon.integrations.oci.spi.OciAtnStrategy;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+
+@Service.Provider
+@Service.ExternalContracts(AbstractAuthenticationDetailsProvider.class)
+class AtnDetailsProvider implements Supplier> {
+
+ private final LazyValue> provider;
+
+ AtnDetailsProvider(OciConfig ociConfig, List atnDetailsProviders) {
+ String chosenStrategy = ociConfig.atnStrategy();
+ LazyValue> providerLazyValue = null;
+
+ if (OciConfigBlueprint.STRATEGY_AUTO.equals(chosenStrategy)) {
+ // auto, chose from existing
+ providerLazyValue = LazyValue.create(() -> {
+ for (OciAtnStrategy atnDetailsProvider : atnDetailsProviders) {
+ Optional provider = atnDetailsProvider.provider();
+ if (provider.isPresent()) {
+ return provider;
+ }
+ }
+ return Optional.empty();
+ });
+ } else {
+ List strategies = new ArrayList<>();
+
+ for (OciAtnStrategy atnDetailsProvider : atnDetailsProviders) {
+ if (chosenStrategy.equals(atnDetailsProvider.strategy())) {
+ providerLazyValue = LazyValue.create(() -> Optional.of(atnDetailsProvider.provider()
+ .orElseThrow(() -> new ConfigException("Strategy \""
+ + chosenStrategy
+ + "\" did not provide an authentication provider, "
+ + "yet it is requested through configuration."))));
+ break;
+ }
+ strategies.add(atnDetailsProvider.strategy());
+ }
+
+ if (providerLazyValue == null) {
+ throw new ConfigException("There is a strategy chosen for OCI authentication: \"" + chosenStrategy
+ + "\", yet there is not provider that can provide that strategy. Supported "
+ + "strategies: " + strategies);
+ }
+ }
+
+ this.provider = providerLazyValue;
+ }
+
+ @Override
+ public Optional get() {
+ return provider.get();
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfig.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfig.java
new file mode 100644
index 00000000000..8c33237de6f
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfig.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.Optional;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.common.configurable.Resource;
+import io.helidon.integrations.oci.spi.OciAtnStrategy;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.Region;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.SimplePrivateKeySupplier;
+
+/**
+ * Config based authentication strategy, uses the {@link com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider}.
+ */
+@Weight(Weighted.DEFAULT_WEIGHT - 10)
+@Service.Provider
+class AtnStrategyConfig implements OciAtnStrategy {
+ static final String STRATEGY = "config";
+
+ private final LazyValue> provider;
+
+ AtnStrategyConfig(OciConfig config) {
+ provider = config.configStrategyConfig()
+ .map(configStrategyConfigBlueprint -> LazyValue.create(() -> {
+ return Optional.of(createProvider(configStrategyConfigBlueprint));
+ }))
+ .orElseGet(() -> LazyValue.create(Optional.empty()));
+ }
+
+ @Override
+ public String strategy() {
+ return STRATEGY;
+ }
+
+ @Override
+ public Optional provider() {
+ return provider.get();
+ }
+
+ private static AbstractAuthenticationDetailsProvider createProvider(ConfigStrategyConfigBlueprint config) {
+ Region region = Region.fromRegionCodeOrId(config.region());
+
+ var builder = SimpleAuthenticationDetailsProvider.builder();
+
+ // private key may be provided through different means
+ if (config.privateKey().isPresent()) {
+ // as a resource (classpath, file system, base64, plain text)
+ Resource resource = config.privateKey().get();
+ builder.privateKeySupplier(resource::stream);
+ } else {
+ // or as the default location in user.home/.oci/oic_api_key.pem
+ String keyFile = System.getProperty("user.home");
+ if (keyFile == null) {
+ keyFile = "/";
+ } else {
+ if (!keyFile.endsWith("/")) {
+ keyFile = keyFile + "/";
+ }
+ }
+ keyFile = keyFile + ".oci/oci_api_key.pem";
+
+ builder.privateKeySupplier(new SimplePrivateKeySupplier(keyFile));
+ }
+
+ return builder.region(region)
+ .tenantId(config.tenantId())
+ .userId(config.userId())
+ .fingerprint(config.fingerprint())
+ .passphraseCharacters(config.passphrase())
+ .build();
+
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfigFile.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfigFile.java
new file mode 100644
index 00000000000..ae39f4e9f34
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfigFile.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.util.Optional;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.integrations.oci.spi.OciAtnStrategy;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.ConfigFileReader;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider;
+
+/**
+ * Config file based authentication strategy, uses the {@link com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider}.
+ */
+@Weight(Weighted.DEFAULT_WEIGHT - 20)
+@Service.Provider
+class AtnStrategyConfigFile implements OciAtnStrategy {
+ static final String DEFAULT_PROFILE_NAME = "DEFAULT";
+ static final String STRATEGY = "config-file";
+
+ private static final System.Logger LOGGER = System.getLogger(AtnStrategyConfigFile.class.getName());
+
+ private final LazyValue> provider;
+
+ AtnStrategyConfigFile(OciConfig config) {
+ provider = createProvider(config);
+ }
+
+ @Override
+ public String strategy() {
+ return STRATEGY;
+ }
+
+ @Override
+ public Optional provider() {
+ return provider.get();
+ }
+
+ private static LazyValue> createProvider(OciConfig config) {
+ return LazyValue.create(() -> {
+ // there are two options to override - the path to config file, and the profile
+ var strategyConfig = config.configFileStrategyConfig();
+ String profile = strategyConfig.map(ConfigFileStrategyConfigBlueprint::profile)
+ .orElse(DEFAULT_PROFILE_NAME);
+ String configFilePath = strategyConfig.flatMap(ConfigFileStrategyConfigBlueprint::path)
+ .orElse(null);
+
+ try {
+ ConfigFileReader.ConfigFile configFile;
+ if (configFilePath == null) {
+ configFile = ConfigFileReader.parseDefault(profile);
+ } else {
+ configFile = ConfigFileReader.parse(configFilePath, profile);
+ }
+ return Optional.of(new ConfigFileAuthenticationDetailsProvider(configFile));
+ } catch (IOException e) {
+ if (LOGGER.isLoggable(Level.TRACE)) {
+ LOGGER.log(Level.TRACE, "Cannot parse config file. Location: " + configFilePath + ", profile: " + profile, e);
+ }
+ return Optional.empty();
+ }
+ });
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyInstancePrincipal.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyInstancePrincipal.java
new file mode 100644
index 00000000000..5a9730f108b
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyInstancePrincipal.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.net.InetAddress;
+import java.net.URI;
+import java.time.Duration;
+import java.util.Optional;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.integrations.oci.spi.OciAtnStrategy;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider;
+
+import static io.helidon.integrations.oci.OciConfigSupport.IMDS_HOSTNAME;
+
+/**
+ * Instance principal authentication strategy, uses the
+ * {@link com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider}.
+ */
+@Weight(Weighted.DEFAULT_WEIGHT - 40)
+@Service.Provider
+class AtnStrategyInstancePrincipal implements OciAtnStrategy {
+ static final String STRATEGY = "instance-principal";
+
+ private static final System.Logger LOGGER = System.getLogger(AtnStrategyInstancePrincipal.class.getName());
+
+ private final LazyValue> provider;
+
+ AtnStrategyInstancePrincipal(OciConfig config) {
+ provider = createProvider(config);
+ }
+
+ @Override
+ public String strategy() {
+ return STRATEGY;
+ }
+
+ @Override
+ public Optional provider() {
+ return provider.get();
+ }
+
+ private static LazyValue> createProvider(OciConfig config) {
+ return LazyValue.create(() -> {
+ if (imdsAvailable(config)) {
+ var builder = InstancePrincipalsAuthenticationDetailsProvider.builder()
+ .timeoutForEachRetry((int) config.atnTimeout().toMillis());
+
+ config.imdsBaseUri()
+ .map(URI::toString)
+ .ifPresent(builder::metadataBaseUrl);
+
+ return Optional.of(builder.build());
+ }
+ return Optional.empty();
+ });
+ }
+
+ static boolean imdsAvailable(OciConfig config) {
+ Duration timeout = config.imdsTimeout();
+
+ try {
+ if (InetAddress.getByName(config.imdsBaseUri().map(URI::getHost).orElse(IMDS_HOSTNAME))
+ .isReachable((int) timeout.toMillis())) {
+ return RegionProviderSdk.regionFromImds(config) != null;
+ }
+ return false;
+ } catch (IOException e) {
+ LOGGER.log(Level.TRACE,
+ "imds service is not reachable, or timed out for address: " + IMDS_HOSTNAME + ", instance principal "
+ + "strategy is not available.",
+ e);
+ return false;
+ }
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java
new file mode 100644
index 00000000000..2b856fc00a5
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.lang.System.Logger.Level;
+import java.net.URI;
+import java.util.Optional;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.integrations.oci.spi.OciAtnStrategy;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider;
+
+/**
+ * Resource authentication strategy, uses the {@link com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider}.
+ */
+@Weight(Weighted.DEFAULT_WEIGHT - 30)
+@Service.Provider
+class AtnStrategyResourcePrincipal implements OciAtnStrategy {
+ static final String RESOURCE_PRINCIPAL_VERSION_ENV_VAR = "OCI_RESOURCE_PRINCIPAL_VERSION";
+ static final String STRATEGY = "resource-principal";
+
+ private static final System.Logger LOGGER = System.getLogger(AtnStrategyResourcePrincipal.class.getName());
+
+ private final LazyValue> provider;
+
+ AtnStrategyResourcePrincipal(OciConfig config) {
+ provider = createProvider(config);
+ }
+
+ @Override
+ public String strategy() {
+ return STRATEGY;
+ }
+
+ @Override
+ public Optional provider() {
+ return provider.get();
+ }
+
+ private static LazyValue> createProvider(OciConfig config) {
+ return LazyValue.create(() -> {
+ // https://github.com/oracle/oci-java-sdk/blob/v2.19.0/bmc-common/src/main/java/com/oracle/bmc/auth/ResourcePrincipalAuthenticationDetailsProvider.java#L246-L251
+ if (System.getenv(RESOURCE_PRINCIPAL_VERSION_ENV_VAR) == null) {
+ if (LOGGER.isLoggable(Level.TRACE)) {
+ LOGGER.log(Level.TRACE, "Environment variable \"" + RESOURCE_PRINCIPAL_VERSION_ENV_VAR
+ + "\" is not set, resource principal cannot be used.");
+ }
+ return Optional.empty();
+ }
+ var builder = ResourcePrincipalAuthenticationDetailsProvider.builder()
+ .timeoutForEachRetry((int) config.atnTimeout().toMillis());
+
+ // we expect the full metadata base URI (including http:// and /opc/v2/)
+ config.imdsBaseUri()
+ .map(URI::toString)
+ .ifPresent(builder::metadataBaseUrl);
+
+ return Optional.of(builder.build());
+ });
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileStrategyConfigBlueprint.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileStrategyConfigBlueprint.java
new file mode 100644
index 00000000000..7ee84c7fb75
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileStrategyConfigBlueprint.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.Optional;
+
+import io.helidon.builder.api.Option;
+import io.helidon.builder.api.Prototype;
+
+@Prototype.Blueprint
+@Prototype.Configured
+interface ConfigFileStrategyConfigBlueprint {
+ /**
+ * The OCI configuration profile path.
+ *
+ * @return the OCI configuration profile path
+ */
+ @Option.Configured
+ Optional path();
+
+ /**
+ * The OCI configuration/auth profile name.
+ *
+ * @return the optional OCI configuration/auth profile name
+ */
+ @Option.Configured
+ @Option.Default(AtnStrategyConfigFile.DEFAULT_PROFILE_NAME)
+ String profile();
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigStrategyConfigBlueprint.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigStrategyConfigBlueprint.java
new file mode 100644
index 00000000000..968688f1587
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigStrategyConfigBlueprint.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.Optional;
+
+import io.helidon.builder.api.Option;
+import io.helidon.builder.api.Prototype;
+import io.helidon.common.configurable.Resource;
+
+/**
+ * Configuration of the {@code config} authentication strategy.
+ */
+@Prototype.Blueprint
+@Prototype.Configured
+interface ConfigStrategyConfigBlueprint {
+ /**
+ * The OCI region.
+ *
+ * @return the OCI region
+ */
+ @Option.Configured
+ String region();
+
+ /**
+ * The OCI authentication fingerprint.
+ *
+ * This configuration property must be provided in order to set the API signing key's fingerprint.
+ * See {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getFingerprint()} for more details.
+ *
+ * @return the OCI authentication fingerprint
+ */
+ @Option.Configured
+ String fingerprint();
+
+ /**
+ * The OCI authentication private key resource.
+ * A resource can be defined as a resource on classpath, file on the file system,
+ * base64 encoded text value in config, or plain-text value in config.
+ *
+ * If not defined, we will use {@code .oci/oic_api_key.pem} file in user home directory.
+ *
+ * @return the OCI authentication key file
+ */
+ @Option.Configured
+ Optional privateKey();
+
+ /**
+ * The OCI authentication passphrase.
+ *
+ * This property must be provided in order to set the
+ * {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPassphraseCharacters()}.
+ *
+ * @return the OCI authentication passphrase
+ */
+ @Option.Configured
+ @Option.Confidential
+ char[] passphrase();
+
+ /**
+ * The OCI tenant id.
+ *
+ * This property must be provided in order to set the
+ * {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getTenantId()}.
+ *
+ * @return the OCI tenant id
+ */
+ @Option.Configured
+ String tenantId();
+
+ /**
+ * The OCI user id.
+ *
+ * This property must be provided in order to set the
+ * {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getUserId()}.
+ *
+ * @return the OCI user id
+ */
+ @Option.Configured
+ String userId();
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigBlueprint.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigBlueprint.java
new file mode 100644
index 00000000000..cdc554afbeb
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigBlueprint.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.net.URI;
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+
+import io.helidon.builder.api.Option;
+import io.helidon.builder.api.Prototype;
+
+import com.oracle.bmc.Region;
+
+/**
+ * Meta configuration of OCI integration for Helidon.
+ *
+ * Allows customization of discovery of authentication details provider and of region.
+ */
+@Prototype.Configured("helidon.oci")
+@Prototype.Blueprint
+@Prototype.CustomMethods(OciConfigSupport.class)
+interface OciConfigBlueprint {
+ /**
+ * Default authentication strategy. The default is to use automatic discovery - i.e. cycle through possible
+ * providers until one yields an authentication details provider instance.
+ */
+ String STRATEGY_AUTO = "auto";
+
+ /**
+ * Explicit region. The configured region will be used by region provider.
+ * This may be ignored by authentication detail providers, as in most cases region is provided by them.
+ *
+ * @return explicit region
+ */
+ @Option.Configured
+ Optional region();
+
+ /**
+ * Authentication strategy to use. If the configured strategy is not available, an exception
+ * would be thrown for OCI related services.
+ *
+ * Known and supported authentication strategies for public OCI:
+ *
+ *
{@value #STRATEGY_AUTO} - use the list of {@link #allowedAtnStrategies()} (in the provided order), and choose
+ * the first one
+ * capable of providing data
+ *
{@value AtnStrategyConfig#STRATEGY} -
+ * use configuration of the application to obtain values needed to set up connectivity, uses
+ * {@link com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider}
+ *
{@value AtnStrategyConfigFile#STRATEGY} - use configuration file of OCI ({@code home/.oci/config}), uses
+ * {@link com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider}
+ *
{@value AtnStrategyResourcePrincipal#STRATEGY} - use identity of the OCI resource the service is executed on
+ * (fn), uses
+ * {@link com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider}
+ *
{@value AtnStrategyInstancePrincipal#STRATEGY} - use identity of the OCI instance the service is running on, uses
+ * {@link com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider}
+ *
+ *
+ * @return the authentication strategy to apply
+ */
+ @Option.Configured
+ @Option.Default(STRATEGY_AUTO)
+ String atnStrategy();
+
+ /**
+ * List of attempted authentication strategies in case {@link #atnStrategy()} is set to {@value #STRATEGY_AUTO}.
+ *
+ * In case the list is empty, all available strategies will be tried, ordered by their {@link io.helidon.common.Weight}
+ *
+ * @return list of authentication strategies to be tried
+ * @see #atnStrategy()
+ */
+ @Option.Configured
+ List allowedAtnStrategies();
+
+ /**
+ * Config strategy configuration (if provided and used).
+ *
+ * @return information needed for config {@link #atnStrategy()}
+ */
+ @Option.Configured("config-strategy")
+ Optional configStrategyConfig();
+
+ /**
+ * Config file strategy configuration (if provided and used).
+ *
+ * @return information to customize config for {@link #atnStrategy()}
+ */
+ @Option.Configured("config-file-strategy")
+ Optional configFileStrategyConfig();
+
+ /**
+ * The OCI IMDS connection timeout. This is used to auto-detect availability.
+ *
+ * This configuration property is used when attempting to connect to the metadata service.
+ *
+ * @return the OCI IMDS connection timeout
+ */
+ @Option.Configured
+ @Option.Default("PT0.1S")
+ Duration imdsTimeout();
+
+ /**
+ * The OCI IMDS URI (http URL pointing to the metadata service, if customization needed.
+ *
+ * @return the OCI IMDS URI
+ */
+ @Option.Configured
+ Optional imdsBaseUri();
+
+ /**
+ * Timeout of authentication operations, where applicable.
+ * This is a timeout for each operation (if there are retries, each timeout will be this duration).
+ * Defaults to 10 seconds.
+ *
+ * @return authentication operation timeout
+ */
+ @Option.Configured
+ @Option.Default("PT10S")
+ Duration atnTimeout();
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigProvider.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigProvider.java
new file mode 100644
index 00000000000..5d0771bc4d9
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigProvider.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Supplier;
+
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.common.config.Config;
+import io.helidon.config.ConfigSources;
+import io.helidon.service.registry.Service;
+
+// the supplier MUST use fully qualified name, as the type is generated as part of annotation processing
+// and the generated contract would be wrong if not
+@Service.Provider
+@Weight(Weighted.DEFAULT_WEIGHT - 10)
+class OciConfigProvider implements Supplier {
+ private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
+ private static volatile OciConfig ociConfig;
+
+ OciConfigProvider() {
+ }
+
+ static void config(OciConfig ociConfig) {
+ LOCK.writeLock().lock();
+ try {
+ OciConfigProvider.ociConfig = ociConfig;
+ } finally {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public OciConfig get() {
+ LOCK.readLock().lock();
+ try {
+ OciConfig toUse = ociConfig;
+ if (toUse != null) {
+ return toUse;
+ }
+ } finally {
+ LOCK.readLock().unlock();
+ }
+ LOCK.writeLock().lock();
+ try {
+ create();
+ return ociConfig;
+ } finally {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ private static void create() {
+ Config config = io.helidon.config.Config.create(
+ ConfigSources.environmentVariables(),
+ ConfigSources.systemProperties(),
+ ConfigSources.file("oci-config.yaml").optional(true),
+ ConfigSources.classpath("oci-config.yaml").optional(true));
+
+ ociConfig = OciConfig.create(config);
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigSupport.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigSupport.java
new file mode 100644
index 00000000000..853e0403939
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/OciConfigSupport.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import io.helidon.builder.api.Prototype;
+import io.helidon.common.config.Config;
+
+import com.oracle.bmc.Region;
+
+final class OciConfigSupport {
+ /**
+ * Primary hostname of metadata service.
+ */
+ // we do not use the constant, as it is marked as internal, and we only need the IP address anyway
+ // see com.oracle.bmc.auth.AbstractFederationClientAuthenticationDetailsProviderBuilder.METADATA_SERVICE_BASE_URL
+ static final String IMDS_HOSTNAME = "169.254.169.254";
+
+ private OciConfigSupport() {
+ }
+
+ @Prototype.FactoryMethod
+ static Region createRegion(Config config) {
+ return config.asString()
+ .map(Region::fromRegionCodeOrId)
+ .get();
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProvider.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProvider.java
new file mode 100644
index 00000000000..5f156ca6864
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import io.helidon.integrations.oci.spi.OciRegion;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.Region;
+
+@Service.Provider
+@Service.ExternalContracts(Region.class)
+class RegionProvider implements Supplier> {
+ private final List regionProviders;
+
+ RegionProvider(List regionProviders) {
+ this.regionProviders = regionProviders;
+ }
+
+ @Override
+ public Optional get() {
+ for (OciRegion regionProvider : regionProviders) {
+ Optional region = regionProvider.region();
+ if (region.isPresent()) {
+ return region;
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAtnStrategy.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAtnStrategy.java
new file mode 100644
index 00000000000..f8707b31726
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAtnStrategy.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.integrations.oci.spi.OciRegion;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.Region;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.RegionProvider;
+
+@Service.Provider
+@Weight(Weighted.DEFAULT_WEIGHT - 20)
+class RegionProviderAtnStrategy implements OciRegion {
+ private final LazyValue> region;
+
+ RegionProviderAtnStrategy(Supplier> atnProvider) {
+
+ this.region = LazyValue.create(() -> {
+ var provider = atnProvider.get();
+ if (provider.isEmpty()) {
+ return Optional.empty();
+ }
+ if (provider.get() instanceof RegionProvider regionProvider) {
+ return Optional.of(regionProvider.getRegion());
+ }
+ return Optional.empty();
+ });
+ }
+
+ @Override
+ public Optional region() {
+ return region.get();
+ }
+}
diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Hammer.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderConfig.java
similarity index 50%
rename from examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Hammer.java
rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderConfig.java
index af4ccb73dfb..1e9ce652772 100644
--- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Hammer.java
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2024 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.
@@ -14,31 +14,30 @@
* limitations under the License.
*/
-package io.helidon.examples.inject.basics;
+package io.helidon.integrations.oci;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import io.helidon.common.LazyValue;
import io.helidon.common.Weight;
import io.helidon.common.Weighted;
+import io.helidon.integrations.oci.spi.OciRegion;
+import io.helidon.service.registry.Service;
-import jakarta.inject.Singleton;
+import com.oracle.bmc.Region;
-/**
- * By adding the {@link Singleton} annotation results in Hammer becoming a service. Services can be looked up
- * programmatically or declaratively injected via {@link jakarta.inject.Inject}.
- *
- * Here {@link Weight} is used that is higher than the default, making it more preferred in weighted rankings.
- */
-@Singleton
-@Weight(Weighted.DEFAULT_WEIGHT + 1)
-class Hammer implements Tool {
+@Service.Provider
+@Weight(Weighted.DEFAULT_WEIGHT - 10)
+class RegionProviderConfig implements OciRegion {
+ private final LazyValue> region;
- @Override
- public String name() {
- return "Hammer";
+ RegionProviderConfig(Supplier config) {
+ this.region = LazyValue.create(() -> config.get().region());
}
@Override
- public String toString() {
- return name();
+ public Optional region() {
+ return region.get();
}
-
}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderSdk.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderSdk.java
new file mode 100644
index 00000000000..9d8034a8abe
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderSdk.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.integrations.oci.spi.OciRegion;
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.Region;
+
+@Service.Provider
+@Weight(Weighted.DEFAULT_WEIGHT - 100)
+class RegionProviderSdk implements OciRegion {
+ private final LazyValue> region;
+
+ RegionProviderSdk(Supplier config) {
+ this.region = LazyValue.create(() -> Optional.ofNullable(regionFromImds(config.get())));
+ }
+
+ /**
+ * There is a 30 second timeout configured, so this has a relatively low weight.
+ * We want a different way to get the region if available.
+ */
+ static Region regionFromImds(OciConfig ociConfig) {
+ if (AtnStrategyInstancePrincipal.imdsAvailable(ociConfig)) {
+ Optional uri = ociConfig.imdsBaseUri();
+ return uri.map(URI::toString)
+ .map(Region::getRegionFromImds)
+ .orElseGet(() -> {
+ Region.registerFromInstanceMetadataService();
+ return Region.getRegionFromImds();
+ });
+
+ }
+ return null;
+ }
+
+ @Override
+ public Optional region() {
+ return region.get();
+ }
+}
diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/package-info.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/package-info.java
similarity index 74%
rename from examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/package-info.java
rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/package-info.java
index fd932e1fa0b..98c875dfdab 100644
--- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/package-info.java
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2024 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.
@@ -15,6 +15,7 @@
*/
/**
- * Examples of providers in Injection.
+ * Implementation of OCI integrations.
+ * The only public classes in this package are service descriptors.
*/
-package io.helidon.examples.inject.providers;
+package io.helidon.integrations.oci;
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciAtnStrategy.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciAtnStrategy.java
new file mode 100644
index 00000000000..44f324dbfab
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciAtnStrategy.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci.spi;
+
+import java.util.Optional;
+
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+
+/**
+ * An OCI Authentication Details Provider service contract.
+ *
+ * This service is implemented by:
+ *
+ *
Config based strategy
+ *
Config file based strategy
+ *
Resource principal strategy
+ *
Instance principal strategy
+ *
+ * The first one that provides an instance will be used as the value.
+ * To customize, create your own service with a default or higher weight.
+ */
+@Service.Contract
+public interface OciAtnStrategy {
+ /**
+ * The strategy name, can be used to explicitly select a strategy using configuration.
+ *
+ * @return strategy name
+ */
+ String strategy();
+
+ /**
+ * Provide an instance of the {@link com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider} to be used
+ * by other services.
+ *
+ * @return authentication details provider, or empty if nothing can be provided
+ */
+ Optional provider();
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciRegion.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciRegion.java
new file mode 100644
index 00000000000..5f6be1ae556
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciRegion.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci.spi;
+
+import java.util.Optional;
+
+import io.helidon.service.registry.Service;
+
+import com.oracle.bmc.Region;
+
+/**
+ * An OCI region discovery mechanism to provide {@link com.oracle.bmc.Region} as a service in Helidon
+ * service registry.
+ *
+ * This service is implemented by:
+ *
+ *
Config based - just put {@code oci.region} to OCI configuration
+ * (or environment variables/system properties)
+ *
Authentication provider based - if the authentication provider implements a
+ * {@link com.oracle.bmc.auth.RegionProvider}, the region will be used
+ *
Region from {@link com.oracle.bmc.Region#getRegionFromImds()}
+ *
+ * The first one that provides an instance will be used as the value.
+ * To customize, create your own service with a default or higher weight.
+ */
+@Service.Contract
+public interface OciRegion {
+ /**
+ * The region, if it can be provided by this service.
+ *
+ * @return OCI region
+ */
+ Optional region();
+}
diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/package-info.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/package-info.java
new file mode 100644
index 00000000000..858027753d0
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+/**
+ * Extension points for OCI integration.
+ *
+ * @see io.helidon.integrations.oci.spi.OciAtnStrategy
+ * @see io.helidon.integrations.oci.spi.OciRegion
+ */
+package io.helidon.integrations.oci.spi;
diff --git a/integrations/oci/oci/src/main/java/module-info.java b/integrations/oci/oci/src/main/java/module-info.java
new file mode 100644
index 00000000000..0175cccaa98
--- /dev/null
+++ b/integrations/oci/oci/src/main/java/module-info.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+/**
+ * OCI integration module using Helidon Service Registry.
+ * This core module provides services for {@link com.oracle.bmc.Region} and
+ * {@link com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider}.
+ *
+ * The module does not require {@link io.helidon.common.config.Config} service to be available, as it is considered
+ * a prerequisite for possible config sources.
+ *
+ *
+ * To customize configuration of this module, the following options exist:
+ *
+ *
Create a custom service provider that provides {@link io.helidon.integrations.oci.OciConfig}
+ *
Create a file {@code oci-config.yaml} either on classpath, or in the current directory with configuration
+ * required by {@link io.helidon.integrations.oci.OciConfig}, this also requires YAML config parser on classpath.
+ *
Add environment variables to override configuration options of {@link io.helidon.integrations.oci.OciConfig},
+ * such as {@code OCI_ATNSTRATEGY=config_file}
+ *
Add system properties to override configuration options of {@link io.helidon.integrations.oci.OciConfig},
+ * such as {@code oci.atnStrategy=config_file}
+ *
+ *
+ * To customize authentication details provider, you can implement {@link io.helidon.integrations.oci.spi.OciAtnStrategy}
+ * service. The out-of-the-box providers have all less than default weight, and are in the
+ * following order (strategy: description (weight)):
+ *
+ *
{@code config}: Config based authentication details provider - using only configured options (default weight - 10)
{@code resource-principal}: Resource principal, used for example by fn (default-weight - 30)
+ *
{@code instance-principal}: Instance principal, used for VMs (default-weight - 40)
+ *
+ */
+module io.helidon.integrations.oci {
+ requires io.helidon.common.configurable;
+ requires io.helidon.service.registry;
+ requires oci.java.sdk.common;
+ requires io.helidon.common.config;
+ requires org.bouncycastle.util;
+ requires io.helidon.config;
+
+ exports io.helidon.integrations.oci;
+ exports io.helidon.integrations.oci.spi;
+}
diff --git a/integrations/oci/oci/src/test/java/io/helidon/integrations/oci/OciIntegrationTest.java b/integrations/oci/oci/src/test/java/io/helidon/integrations/oci/OciIntegrationTest.java
new file mode 100644
index 00000000000..513f465bde2
--- /dev/null
+++ b/integrations/oci/oci/src/test/java/io/helidon/integrations/oci/OciIntegrationTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2024 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.integrations.oci;
+
+import java.util.Optional;
+
+import io.helidon.common.media.type.MediaTypes;
+import io.helidon.config.Config;
+import io.helidon.config.ConfigSources;
+import io.helidon.integrations.oci.spi.OciAtnStrategy;
+import io.helidon.service.registry.ServiceRegistry;
+import io.helidon.service.registry.ServiceRegistryManager;
+
+import com.oracle.bmc.Region;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static io.helidon.common.testing.junit5.OptionalMatcher.optionalEmpty;
+import static io.helidon.common.testing.junit5.OptionalMatcher.optionalValue;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.not;
+
+class OciIntegrationTest {
+
+ private ServiceRegistryManager registryManager;
+ private ServiceRegistry registry;
+
+ void setUp(Config config) {
+ OciConfigProvider.config(OciConfig.create(config.get("helidon.oci")));
+ registryManager = ServiceRegistryManager.create();
+ registry = registryManager.registry();
+ }
+
+ @AfterEach
+ void tearDown() {
+ registry = null;
+ if (registryManager != null) {
+ registryManager.shutdown();
+ }
+ }
+
+ @Test
+ void testNoStrategyAvailable() {
+ String yamlConfig = """
+ helidon.oci:
+ config-file-strategy:
+ # we must use a file that does not exist, if this machine has actual oci config file
+ path: src/test/resources/test-oci-config-not-there
+ """;
+ Config config = Config.just(ConfigSources.create(yamlConfig, MediaTypes.APPLICATION_YAML));
+ setUp(config);
+
+ OciAtnStrategy atnStrategy = registry.get(AtnStrategyConfig.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyConfig.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyConfigFile.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyConfigFile.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyInstancePrincipal.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyInstancePrincipal.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyResourcePrincipal.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyResourcePrincipal.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ assertThat(registry.first(Region.class), is(Optional.empty()));
+ assertThat(registry.first(AbstractAuthenticationDetailsProvider.class), is(Optional.empty()));
+ }
+
+ @Test
+ void testRegionFromConfig() {
+ String yamlConfig = """
+ helidon.oci.region: us-phoenix-1
+ config-file-strategy:
+ # we must use a file that does not exist, if this machine has actual oci config file
+ path: src/test/resources/test-oci-config-not-there
+ """;
+ Config config = Config.just(ConfigSources.create(yamlConfig, MediaTypes.APPLICATION_YAML));
+ setUp(config);
+
+ assertThat(registry.first(Region.class), optionalValue(is(Region.US_PHOENIX_1)));
+ }
+
+ @Test
+ void testConfigStrategyAvailable() {
+ String yamlConfig = """
+ helidon.oci:
+ config-strategy:
+ # region must be real, so it can be parsed
+ region: us-phoenix-1
+ fingerprint: fp
+ passphrase: passphrase
+ tenant-id: tenant
+ user-id: user
+ config-file-strategy:
+ # we must use a file that does not exist, if this machine has actual oci config file
+ path: src/test/resources/test-oci-config-not-there
+ """;
+ Config config = Config.just(ConfigSources.create(yamlConfig, MediaTypes.APPLICATION_YAML));
+ setUp(config);
+
+ OciAtnStrategy atnStrategy = registry.get(AtnStrategyConfig.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyConfig.STRATEGY));
+ assertThat(atnStrategy.provider(), not(Optional.empty()));
+
+ atnStrategy = registry.get(AtnStrategyConfigFile.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyConfigFile.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyInstancePrincipal.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyInstancePrincipal.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyResourcePrincipal.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyResourcePrincipal.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ AbstractAuthenticationDetailsProvider provider = registry.get(AbstractAuthenticationDetailsProvider.class);
+
+ assertThat(provider, instanceOf(SimpleAuthenticationDetailsProvider.class));
+ SimpleAuthenticationDetailsProvider auth = (SimpleAuthenticationDetailsProvider) provider;
+ assertThat(auth.getTenantId(),
+ equalTo("tenant"));
+ assertThat(auth.getUserId(),
+ equalTo("user"));
+ assertThat(auth.getRegion(),
+ equalTo(Region.US_PHOENIX_1));
+ assertThat(new String(auth.getPassphraseCharacters()),
+ equalTo("passphrase"));
+
+ assertThat(registry.first(Region.class), optionalValue(is(Region.US_PHOENIX_1)));
+ }
+
+ @Test
+ void testConfigFileStrategyAvailable() {
+ String yamlConfig = """
+ helidon.oci:
+ config-file-strategy:
+ path: src/test/resources/test-oci-config
+ profile: MY_PROFILE
+ """;
+ Config config = Config.just(ConfigSources.create(yamlConfig, MediaTypes.APPLICATION_YAML));
+ setUp(config);
+
+ OciAtnStrategy atnStrategy = registry.get(AtnStrategyConfig.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyConfig.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyConfigFile.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyConfigFile.STRATEGY));
+ assertThat(atnStrategy.provider(), not(Optional.empty()));
+
+ atnStrategy = registry.get(AtnStrategyInstancePrincipal.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyInstancePrincipal.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ atnStrategy = registry.get(AtnStrategyResourcePrincipal.class);
+ assertThat(atnStrategy.strategy(), is(AtnStrategyResourcePrincipal.STRATEGY));
+ assertThat(atnStrategy.provider(), optionalEmpty());
+
+ AbstractAuthenticationDetailsProvider provider = registry.get(AbstractAuthenticationDetailsProvider.class);
+
+ assertThat(provider, instanceOf(ConfigFileAuthenticationDetailsProvider.class));
+ ConfigFileAuthenticationDetailsProvider auth = (ConfigFileAuthenticationDetailsProvider) provider;
+ assertThat(auth.getTenantId(),
+ equalTo("tenant"));
+ assertThat(auth.getUserId(),
+ equalTo("user"));
+ assertThat(auth.getRegion(),
+ equalTo(Region.US_PHOENIX_1));
+ assertThat(new String(auth.getPassphraseCharacters()),
+ equalTo("passphrase"));
+
+ assertThat(registry.first(Region.class), optionalValue(is(Region.US_PHOENIX_1)));
+ }
+}
diff --git a/examples/inject/configdriven/src/main/resources/application.yaml b/integrations/oci/oci/src/test/resources/test-oci-config
similarity index 63%
rename from examples/inject/configdriven/src/main/resources/application.yaml
rename to integrations/oci/oci/src/test/resources/test-oci-config
index 45b8e6e8af8..ebae2ab8787 100644
--- a/examples/inject/configdriven/src/main/resources/application.yaml
+++ b/integrations/oci/oci/src/test/resources/test-oci-config
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2024 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.
@@ -14,12 +14,17 @@
# limitations under the License.
#
-# these are only needed for unit testing - if there is a repeated setup and tear down, etc.
-inject:
- permits-dynamic: true
+# Used from io.helidon.integrations.oci.OciAuthenticationDetailsProviderTest
+[DEFAULT]
+user=defUser
+fingerprint=deffp
+tenancy=deftenant
+region=eu-frankfurt-1
-drill:
- hand:
- name: "Hand"
- impact:
- name: "Impact"
+[MY_PROFILE]
+user=user
+fingreprint=fp
+tenancy=tenant
+region=us-phoenix-1
+key_file=key.pem
+pass_phrase=passphrase
diff --git a/integrations/oci/pom.xml b/integrations/oci/pom.xml
index 98bf17a7e7d..cf8b46b4133 100644
--- a/integrations/oci/pom.xml
+++ b/integrations/oci/pom.xml
@@ -33,9 +33,10 @@
pom
+ ocimetrics
- oci-secrets-config-source
- oci-secrets-mp-config-source
+ secrets-config-source
+ secrets-mp-config-sourcesdktls-certificates
diff --git a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAuthenticationDetailsProvider.java b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAuthenticationDetailsProvider.java
index 8df6c824070..d2b5a8d400f 100644
--- a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAuthenticationDetailsProvider.java
+++ b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAuthenticationDetailsProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -205,7 +205,7 @@ enum AuthStrategy {
*/
CONFIG_FILE(VAL_CONFIG_FILE,
ConfigFileAuthenticationDetailsProvider.class,
- (configBean) -> configBean.fileConfigIsPresent()
+ (configBean) -> configBean.fileConfigIsPresent()
&& (configBean.configPath().isEmpty() || canReadPath(configBean.configPath().orElse(null))),
(configBean) -> {
// https://github.com/oracle/oci-java-sdk/blob/master/bmc-common/src/main/java/com/oracle/bmc/auth/ConfigFileAuthenticationDetailsProvider.java
diff --git a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAvailability.java b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAvailability.java
index ed4a3872793..3577ce1817f 100644
--- a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAvailability.java
+++ b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciAvailability.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -22,8 +22,10 @@
* Provides a convenient contract for checking whether the current runtime environment is running on/inside an OCI compute node.
*
* @see OciExtension
+ * @deprecated replaced with {@code helidon-integrations-oci} module
*/
@Contract
+@Deprecated(forRemoval = true, since = "4.1.0")
public interface OciAvailability {
/**
diff --git a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciConfigBlueprint.java b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciConfigBlueprint.java
index 542a443abbd..4731a55ce5f 100644
--- a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciConfigBlueprint.java
+++ b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciConfigBlueprint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -87,6 +87,8 @@
// note: this is intended to be a replica to the properties carried from the cdi integrations previously done for MP
@Prototype.Blueprint
@Configured(root = true, prefix = OciConfigBlueprint.CONFIG_KEY)
+@Deprecated(forRemoval = true, since = "4.1.0")
+@Prototype.Annotated("java.lang.Deprecated(forRemoval = true, since = \"4.1.0\")")
interface OciConfigBlueprint {
/**
* Config key of this config.
diff --git a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciExtension.java b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciExtension.java
index 1c704bde419..a4823d6fafb 100644
--- a/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciExtension.java
+++ b/integrations/oci/sdk/runtime/src/main/java/io/helidon/integrations/oci/sdk/runtime/OciExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -118,7 +118,10 @@
* @see Oracle Cloud Infrastructure Java SDK
+ *
+ * @deprecated replaced with {@code helidon-integrations-oci} module
*/
+@Deprecated(forRemoval = true, since = "4.1.0")
public final class OciExtension {
/**
* The name for the OCI bootstrap configuration file (value = {@value}).
diff --git a/integrations/oci/sdk/runtime/src/main/java/module-info.java b/integrations/oci/sdk/runtime/src/main/java/module-info.java
index 3cc42c3c7d0..2ba16341560 100644
--- a/integrations/oci/sdk/runtime/src/main/java/module-info.java
+++ b/integrations/oci/sdk/runtime/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -16,7 +16,10 @@
/**
* Helidon Injection Integrations to support OCI Runtime module.
+ *
+ * @deprecated replaced with {@code helidon-integrations-oci} module
*/
+@Deprecated(forRemoval = true, since = "4.1.0")
module io.helidon.integrations.oci.sdk.runtime {
requires io.helidon.builder.api;
diff --git a/integrations/oci/sdk/tests/test-application/pom.xml b/integrations/oci/sdk/tests/test-application/pom.xml
index e4423eb08f1..067ad22dc27 100644
--- a/integrations/oci/sdk/tests/test-application/pom.xml
+++ b/integrations/oci/sdk/tests/test-application/pom.xml
@@ -113,35 +113,6 @@
-
- io.helidon.inject
- helidon-inject-maven-plugin
- ${helidon.version}
-
-
- compile
- compile
-
- application-create
-
-
-
- testCompile
- test-compile
-
- test-application-create
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/integrations/oci/sdk/tests/test-module1/pom.xml b/integrations/oci/sdk/tests/test-module1/pom.xml
index 2bac814fdb7..14d01da15c9 100644
--- a/integrations/oci/sdk/tests/test-module1/pom.xml
+++ b/integrations/oci/sdk/tests/test-module1/pom.xml
@@ -40,20 +40,6 @@
io.helidon.integrations.oci.sdkhelidon-integrations-oci-sdk-runtime
-
- io.helidon.inject
- helidon-inject-runtime
-
-
- jakarta.annotation
- jakarta.annotation-api
- provided
-
-
- jakarta.inject
- jakarta.inject-api
- provided
- io.helidon.common.testinghelidon-common-testing-junit5
diff --git a/integrations/oci/sdk/tests/test-module2/pom.xml b/integrations/oci/sdk/tests/test-module2/pom.xml
index c942c4dbf6c..4366cdfe007 100644
--- a/integrations/oci/sdk/tests/test-module2/pom.xml
+++ b/integrations/oci/sdk/tests/test-module2/pom.xml
@@ -40,20 +40,6 @@
io.helidon.integrations.oci.sdkhelidon-integrations-oci-sdk-runtime
-
- io.helidon.inject
- helidon-inject-runtime
-
-
- jakarta.annotation
- jakarta.annotation-api
- provided
-
-
- jakarta.inject
- jakarta.inject-api
- provided
- io.helidon.common.testinghelidon-common-testing-junit5
diff --git a/integrations/oci/oci-secrets-config-source/pom.xml b/integrations/oci/secrets-config-source/pom.xml
similarity index 94%
rename from integrations/oci/oci-secrets-config-source/pom.xml
rename to integrations/oci/secrets-config-source/pom.xml
index 7c3965e2432..4c864284738 100644
--- a/integrations/oci/oci-secrets-config-source/pom.xml
+++ b/integrations/oci/secrets-config-source/pom.xml
@@ -50,8 +50,12 @@
helidon-config
- io.helidon.integrations.oci.sdk
- helidon-integrations-oci-sdk-runtime
+ io.helidon.service
+ helidon-service-registry
+
+
+ io.helidon.integrations.oci
+ helidon-integrations-ocicom.oracle.oci.sdk
@@ -92,7 +96,6 @@
-
io.helidon.confighelidon-config-yaml-mp
diff --git a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java
similarity index 70%
rename from integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java
rename to integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java
index f0cc210d931..3e3bf14ffb3 100644
--- a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java
+++ b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/AbstractSecretBundleConfigSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -27,12 +27,12 @@
import io.helidon.config.Config;
import io.helidon.config.ConfigException;
import io.helidon.config.spi.ConfigNode.ValueNode;
+import io.helidon.service.registry.GlobalServiceRegistry;
-import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
import com.oracle.bmc.secrets.Secrets;
import com.oracle.bmc.secrets.SecretsClient;
-import static io.helidon.integrations.oci.sdk.runtime.OciExtension.ociAuthenticationProvider;
import static java.lang.System.Logger.Level.WARNING;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -41,18 +41,15 @@
* and {@link SecretBundleNodeConfigSource}.
*
* @param the type of {@link AbstractConfigSourceBuilder} subclass used to build instances of this class
- *
* @see SecretBundleLazyConfigSource
- *
* @see SecretBundleNodeConfigSource
*/
-public abstract sealed class AbstractSecretBundleConfigSource>
- extends AbstractConfigSource
- permits SecretBundleLazyConfigSource, SecretBundleNodeConfigSource {
-
- private static final Logger LOGGER = System.getLogger(AbstractSecretBundleConfigSource.class.getName());
+abstract sealed class AbstractSecretBundleConfigSource>
+ extends AbstractConfigSource
+ permits SecretBundleLazyConfigSource, SecretBundleNodeConfigSource {
static final String VAULT_OCID_PROPERTY_NAME = "vault-ocid";
+ private static final Logger LOGGER = System.getLogger(AbstractSecretBundleConfigSource.class.getName());
/**
* Creates a new {@link AbstractSecretBundleConfigSource}.
@@ -74,8 +71,8 @@ static ValueNode valueNode(String base64EncodedContent, Base64.Decoder base64Dec
* @param the builder subclass
*/
public abstract static sealed class Builder>
- extends AbstractConfigSourceBuilder
- permits SecretBundleLazyConfigSource.Builder, SecretBundleNodeConfigSource.Builder {
+ extends AbstractConfigSourceBuilder
+ permits SecretBundleLazyConfigSource.Builder, SecretBundleNodeConfigSource.Builder {
private Supplier extends Secrets> secretsSupplier;
@@ -90,36 +87,39 @@ protected Builder() {
this.secretsSupplier = () -> scb.build(adpSupplier().get());
}
+ static LazyValue extends AbstractAuthenticationDetailsProvider> adpSupplier() {
+ return LazyValue.create(() -> GlobalServiceRegistry.registry()
+ .get(AbstractAuthenticationDetailsProvider.class));
+ }
+
/**
* Configures this {@link Builder} from the supplied meta-configuration.
*
* @param metaConfig the meta-configuration; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code metaConfig} is {@code null}
+ * @throws NullPointerException if {@code metaConfig} is {@code null}
*/
@Override // AbstractConfigSourceBuilder
public B config(Config metaConfig) {
metaConfig.get("change-watcher")
- .asNode()
- .ifPresent(n -> {
+ .asNode()
+ .ifPresent(n -> {
throw new ConfigException("Invalid meta-configuration key: change-watcher: "
- + "Change watching is not supported by "
- + this.getClass().getName() + " instances");
+ + "Change watching is not supported by "
+ + this.getClass().getName() + " instances");
});
metaConfig.get("vault-ocid")
- .asString()
- .filter(Predicate.not(String::isBlank))
- .ifPresentOrElse(this::vaultOcid,
- () -> {
- if (LOGGER.isLoggable(WARNING)) {
- LOGGER.log(WARNING,
- "No meta-configuration value supplied for "
- + metaConfig.key().toString() + "." + VAULT_OCID_PROPERTY_NAME
- + "); resulting ConfigSource will be empty");
- }
- });
+ .asString()
+ .filter(Predicate.not(String::isBlank))
+ .ifPresentOrElse(this::vaultOcid,
+ () -> {
+ if (LOGGER.isLoggable(WARNING)) {
+ LOGGER.log(WARNING,
+ "No meta-configuration value supplied for "
+ + metaConfig.key().toString() + "." + VAULT_OCID_PROPERTY_NAME
+ + "); resulting ConfigSource will be empty");
+ }
+ });
return super.config(metaConfig);
}
@@ -128,10 +128,8 @@ public B config(Config metaConfig) {
* retrieve values.
*
* @param vaultOcid a valid OCID identifying an OCI vault; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code vaultId} is {@code null}
+ * @throws NullPointerException if {@code vaultId} is {@code null}
*/
@SuppressWarnings("unchecked")
public B vaultOcid(String vaultOcid) {
@@ -139,19 +137,13 @@ public B vaultOcid(String vaultOcid) {
return (B) this;
}
- String vaultOcid() {
- return this.vaultOcid;
- }
-
/**
* Uses the supplied {@link Supplier} of {@link Secrets} instances, instead of the default one, for
* communicating with the OCI Secrets Retrieval API.
*
* @param secretsSupplier the non-default {@link Supplier} to use; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code secretsSupplier} is {@code null}
+ * @throws NullPointerException if {@code secretsSupplier} is {@code null}
*/
@SuppressWarnings("unchecked")
public B secretsSupplier(Supplier extends Secrets> secretsSupplier) {
@@ -159,12 +151,12 @@ public B secretsSupplier(Supplier extends Secrets> secretsSupplier) {
return (B) this;
}
- Supplier extends Secrets> secretsSupplier() {
- return this.secretsSupplier;
+ String vaultOcid() {
+ return this.vaultOcid;
}
- static LazyValue extends BasicAuthenticationDetailsProvider> adpSupplier() {
- return LazyValue.create(() -> (BasicAuthenticationDetailsProvider) ociAuthenticationProvider().get());
+ Supplier extends Secrets> secretsSupplier() {
+ return this.secretsSupplier;
}
}
diff --git a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java
similarity index 86%
rename from integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java
rename to integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java
index 3f82b304047..78f27678bc4 100644
--- a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java
+++ b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/OciSecretsConfigSourceProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -40,13 +40,12 @@
*
*
Ensure you have an authentication mechanism set up to connect to OCI (e.g. a valid OCI configuration
- * file). Authentication with OCI is accomplished via the {@link
- * io.helidon.integrations.oci.sdk.runtime.OciExtension} class; please see its documentation for how and when to set up
- * an {@code oci.yaml} classpath resource to further refine the mechanism of authentication.
+ * file). Authentication with OCI is accomplished via the {@code helidon-integrations-oci} module;
+ * please see its documentation.
*
- *
Ensure there is a classpath resource present named {@code meta-config.yaml}.
+ *
Ensure there is a classpath resource, or a file present named {@code meta-config.yaml}.
*
- *
Ensure the {@code meta-config.yaml} classpath resource contains a {@code sources} element with a {@code type} of
+ *
Ensure the {@code meta-config.yaml} contains a {@code sources} element with a {@code type} of
* {@code oci-secrets} that looks similar to the following, substituting values as appropriate:
sources:
* - type: 'oci-secrets'
* properties:
@@ -62,36 +61,17 @@
@Weight(300D) // a higher weight than the default (100D)
public final class OciSecretsConfigSourceProvider implements ConfigSourceProvider {
-
- /*
- * Static fields.
- */
-
-
private static final Set SUPPORTED_TYPES = Set.of("oci-secrets");
-
- /*
- * Constructors.
- */
-
-
/**
* Creates a new {@link OciSecretsConfigSourceProvider}.
*
* @deprecated For use by {@link java.util.ServiceLoader} only.
*/
- @Deprecated // For use by java.util.ServiceLoader only.
+ @Deprecated
public OciSecretsConfigSourceProvider() {
- super();
}
-
- /*
- * Instance methods.
- */
-
-
/**
* Creates and returns a non-{@code null} {@link AbstractConfigSource} implementation that sources its values from
* an Oracle Cloud Infrastructure (OCI) supported() {
return SUPPORTED_TYPES;
@@ -155,7 +135,7 @@ public Set supported() {
*
* @deprecated For use by the Helidon Config subsystem only.
*/
- @Deprecated // For use by the Helidon Config subsystem only.
+ @Deprecated
@Override // ConfigSourceProvider
public boolean supports(String type) {
return this.supported().contains(type);
diff --git a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java
similarity index 78%
rename from integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java
rename to integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java
index e695f48f52d..da5e907814c 100644
--- a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java
+++ b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleLazyConfigSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -45,30 +45,12 @@
* href="https://docs.oracle.com/en-us/iaas/tools/java/latest/com/oracle/bmc/vault/package-summary.html">Vault APIs.
*/
public final class SecretBundleLazyConfigSource
- extends AbstractSecretBundleConfigSource
- implements LazyConfigSource {
-
-
- /*
- * Static fields.
- */
-
+ extends AbstractSecretBundleConfigSource
+ implements LazyConfigSource {
private static final Logger LOGGER = System.getLogger(SecretBundleLazyConfigSource.class.getName());
-
- /*
- * Instance fields.
- */
-
-
- private final Function super String, ? extends Optional> nodeFunction;
-
-
- /*
- * Constructors.
- */
-
+ private final Function> nodeFunction;
private SecretBundleLazyConfigSource(Builder b) {
super(b);
@@ -81,24 +63,6 @@ private SecretBundleLazyConfigSource(Builder b) {
}
}
-
- /*
- * Instance methods.
- */
-
-
- @Deprecated // For use by the Helidon Config subsystem only.
- @Override // NodeConfigSource
- public Optional node(String key) {
- return this.nodeFunction.apply(key);
- }
-
-
- /*
- * Static methods.
- */
-
-
/**
* Creates and returns a new {@link Builder} for {@linkplain Builder#build() building} {@link
* SecretBundleLazyConfigSource} instances.
@@ -109,6 +73,27 @@ public static Builder builder() {
return new Builder();
}
+ static Optional node(Supplier> secretBundleContentDetailsSupplier) {
+ Object secretBundleContentDetails = secretBundleContentDetailsSupplier.get();
+ if (secretBundleContentDetails instanceof Base64SecretBundleContentDetails base64SecretBundleContentDetails) {
+ return Optional.of(valueNode(base64SecretBundleContentDetails.getContent(), Base64.getDecoder()));
+ }
+ return Optional.empty();
+ }
+
+ static GetSecretBundleByNameRequest request(String vaultOcid, String secretName) {
+ return GetSecretBundleByNameRequest.builder()
+ .vaultId(vaultOcid)
+ .secretName(secretName)
+ .build();
+ }
+
+ @Deprecated // For use by the Helidon Config subsystem only.
+ @Override // NodeConfigSource
+ public Optional node(String key) {
+ return this.nodeFunction.apply(key);
+ }
+
private static Optional node(Pattern acceptPattern,
LazyValue extends Secrets> secretsSupplier,
String vaultOcid,
@@ -116,9 +101,9 @@ private static Optional node(Pattern acceptPattern,
if (!acceptPattern.matcher(secretName).matches()) {
if (LOGGER.isLoggable(DEBUG)) {
LOGGER.log(DEBUG, "Ignoring ConfigNode request for name "
- + secretName
- + " because it was not matched by "
- + acceptPattern);
+ + secretName
+ + " because it was not matched by "
+ + acceptPattern);
}
return Optional.empty();
}
@@ -140,75 +125,28 @@ private static Object secretBundleContentDetails(Secrets s, String vaultOcid, St
}
}
- static Optional node(Supplier> secretBundleContentDetailsSupplier) {
- Object secretBundleContentDetails = secretBundleContentDetailsSupplier.get();
- if (secretBundleContentDetails instanceof Base64SecretBundleContentDetails base64SecretBundleContentDetails) {
- return Optional.of(valueNode(base64SecretBundleContentDetails.getContent(), Base64.getDecoder()));
- }
- return Optional.empty();
- }
-
- static GetSecretBundleByNameRequest request(String vaultOcid, String secretName) {
- return GetSecretBundleByNameRequest.builder()
- .vaultId(vaultOcid)
- .secretName(secretName)
- .build();
- }
-
-
- /*
- * Inner and nested classes.
- */
-
-
/**
* An {@link AbstractSecretBundleConfigSource.Builder} that {@linkplain #build() builds} {@link
* SecretBundleLazyConfigSource} instances.
*/
public static final class Builder extends AbstractSecretBundleConfigSource.Builder {
-
- /*
- * Static fields.
- */
-
-
private static final Pattern ACCEPT_EVERYTHING_PATTERN = Pattern.compile("^.*$");
-
- /*
- * Instance fields.
- */
-
-
private Pattern acceptPattern;
-
- /*
- * Constructors.
- */
-
-
private Builder() {
super();
this.acceptPattern = ACCEPT_EVERYTHING_PATTERN;
}
-
- /*
- * Instance methods.
- */
-
-
/**
* Sets the {@link Pattern} that will dictate which configuration property names are allowed to reach a {@link
* SecretBundleLazyConfigSource} instance.
*
* @param acceptPattern the {@link Pattern}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code acceptPattern} is {@code null}
+ * @throws NullPointerException if {@code acceptPattern} is {@code null}
*/
public Builder acceptPattern(Pattern acceptPattern) {
this.acceptPattern = Objects.requireNonNull(acceptPattern, "acceptPattern");
@@ -229,29 +167,25 @@ public SecretBundleLazyConfigSource build() {
* Configures this {@link Builder} from the supplied meta-configuration.
*
* @param metaConfig the meta-configuration; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception io.helidon.config.ConfigException if a {@code change-watcher} or {@code polling-strategy} is
- * specified
- *
- * @exception NullPointerException if {@code metaConfig} is {@code null}
- *
- * @exception java.util.regex.PatternSyntaxException if the {@code accept-pattern} key's value could not be
- * {@linkplain Pattern#compile(String) compiled}
+ * @throws io.helidon.config.ConfigException if a {@code change-watcher} or {@code polling-strategy} is
+ * specified
+ * @throws NullPointerException if {@code metaConfig} is {@code null}
+ * @throws java.util.regex.PatternSyntaxException if the {@code accept-pattern} key's value could not be
+ * {@linkplain Pattern#compile(String) compiled}
*/
@Override // AbstractSecretBundleConfigSource.Builder
public Builder config(Config metaConfig) {
metaConfig.get("polling-strategy")
- .asNode()
- .ifPresent(n -> {
+ .asNode()
+ .ifPresent(n -> {
throw new ConfigException("Invalid meta-configuration key: polling-strategy: "
- + "Polling is not supported by "
- + this.getClass().getName() + " instances");
+ + "Polling is not supported by "
+ + this.getClass().getName() + " instances");
});
metaConfig.get("accept-pattern")
- .asString()
- .ifPresent(s -> this.acceptPattern(Pattern.compile(s)));
+ .asString()
+ .ifPresent(s -> this.acceptPattern(Pattern.compile(s)));
return super.config(metaConfig);
}
diff --git a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java
similarity index 76%
rename from integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java
rename to integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java
index 2c987a9edd6..27c7bcf7d60 100644
--- a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java
+++ b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/SecretBundleNodeConfigSource.java
@@ -69,37 +69,14 @@
* href="https://docs.oracle.com/en-us/iaas/tools/java/latest/com/oracle/bmc/vault/package-summary.html">Vault APIs.
*/
public final class SecretBundleNodeConfigSource
- extends AbstractSecretBundleConfigSource
- implements NodeConfigSource, PollableSource {
-
-
- /*
- * Static fields.
- */
-
-
- private static final Optional ABSENT_NODE_CONTENT =
- Optional.of(NodeContent.builder().node(ObjectNode.empty()).build());
+ extends AbstractSecretBundleConfigSource
+ implements NodeConfigSource, PollableSource {
private static final String COMPARTMENT_OCID_PROPERTY_NAME = "compartment-ocid";
-
private static final Logger LOGGER = System.getLogger(SecretBundleNodeConfigSource.class.getName());
-
- /*
- * Instance fields.
- */
-
-
- private final Supplier extends Optional> loader;
-
- private final Supplier extends Stamp> stamper;
-
-
- /*
- * Constructors.
- */
-
+ private final Supplier> loader;
+ private final Supplier stamper;
private SecretBundleNodeConfigSource(Builder b) {
super(b);
@@ -107,14 +84,14 @@ private SecretBundleNodeConfigSource(Builder b) {
Supplier extends Vaults> vaultsSupplier = Objects.requireNonNull(b.vaultsSupplier, "b.vaultsSupplier");
String vaultOcid = b.vaultOcid();
if (b.compartmentOcid == null || vaultOcid == null) {
- this.loader = this::absentNodeContent;
+ this.loader = Optional::empty;
this.stamper = Stamp::new;
} else {
ListSecretsRequest listSecretsRequest = ListSecretsRequest.builder()
- .compartmentId(b.compartmentOcid)
- .lifecycleState(LifecycleState.Active)
- .vaultId(vaultOcid)
- .build();
+ .compartmentId(b.compartmentOcid)
+ .lifecycleState(LifecycleState.Active)
+ .vaultId(vaultOcid)
+ .build();
this.loader = () -> this.load(vaultsSupplier, secretsSupplier, listSecretsRequest);
this.stamper = () -> toStamp(secretSummaries(vaultsSupplier, listSecretsRequest), secretsSupplier);
}
@@ -125,104 +102,17 @@ private SecretBundleNodeConfigSource(Builder b) {
* Instance methods.
*/
-
/**
- * Returns {@code true} if the values in this {@link SecretBundleNodeConfigSource} have been modified.
- *
- * @param lastKnownStamp a {@link Stamp}
+ * Creates and returns a new {@link Builder} for {@linkplain Builder#build() building} {@link
+ * SecretBundleNodeConfigSource} instances.
*
- * @return {@code true} if modified
- */
- @Deprecated // For use by the Helidon Config subsystem only.
- @Override // PollableSource
- public boolean isModified(Stamp lastKnownStamp) {
- Stamp stamp = this.stamper.get();
- if (!stamp.eTags().equals(lastKnownStamp.eTags())) {
- return true;
- }
- return stamp.earliestExpiration().isBefore(lastKnownStamp.earliestExpiration());
- }
-
- @Deprecated // For use by the Helidon Config subsystem only.
- @Override // NodeConfigSource
- public Optional load() {
- return this.loader.get();
- }
-
- @Deprecated // For use by the Helidon Config subsystem only.
- @Override // PollableSource
- public Optional pollingStrategy() {
- return super.pollingStrategy();
- }
-
- private Optional absentNodeContent() {
- return ABSENT_NODE_CONTENT;
- }
-
- private Optional load(Supplier extends Vaults> vaultsSupplier,
- Supplier extends Secrets> secretsSupplier,
- ListSecretsRequest listSecretsRequest) {
- Collection extends SecretSummary> secretSummaries = secretSummaries(vaultsSupplier, listSecretsRequest);
- return this.load(secretSummaries, secretsSupplier);
- }
-
- private Optional load(Collection extends SecretSummary> secretSummaries,
- Supplier extends Secrets> secretsSupplier) {
- if (secretSummaries.isEmpty()) {
- return this.absentNodeContent();
- }
- ConcurrentMap valueNodes = new ConcurrentHashMap<>();
- Set eTags = ConcurrentHashMap.newKeySet();
- Collection> tasks = new ArrayList<>(secretSummaries.size());
- Base64.Decoder decoder = Base64.getDecoder();
- Secrets secrets = secretsSupplier.get();
- for (SecretSummary ss : secretSummaries) {
- tasks.add(task(valueNodes::put,
- eTags::add,
- ss.getSecretName(),
- secrets::getSecretBundle,
- ss.getId(),
- decoder));
- }
- completeTasks(tasks, secrets);
- ObjectNode.Builder objectNodeBuilder = ObjectNode.builder();
- for (Entry e : valueNodes.entrySet()) {
- objectNodeBuilder.addValue(e.getKey(), e.getValue());
- }
- return Optional.of(NodeContent.builder()
- .node(objectNodeBuilder.build())
- .stamp(toStamp(secretSummaries, eTags))
- .build());
- }
-
-
- /*
- * Static methods.
+ * @return a new {@link Builder}
*/
-
-
- private static Stamp toStamp(Collection extends SecretSummary> secretSummaries,
- Supplier extends Secrets> secretsSupplier) {
- if (secretSummaries.isEmpty()) {
- return new Stamp();
- }
- Set eTags = ConcurrentHashMap.newKeySet();
- Collection> tasks = new ArrayList<>(secretSummaries.size());
- Secrets secrets = secretsSupplier.get();
- for (SecretSummary ss : secretSummaries) {
- if (ss.getLifecycleState() == LifecycleState.Active) {
- tasks.add(() -> {
- GetSecretBundleResponse response = secrets.getSecretBundle(request(ss.getId()));
- eTags.add(response.getEtag());
- return null; // Callable; null is the only possible return value
- });
- }
- }
- completeTasks(tasks, secrets);
- return toStamp(secretSummaries, eTags);
+ public static Builder builder() {
+ return new Builder();
}
- static Stamp toStamp(Collection extends SecretSummary> secretSummaries, Set extends String> eTags) {
+ static Stamp toStamp(Collection secretSummaries, Set eTags) {
if (secretSummaries.isEmpty()) {
return new Stamp();
}
@@ -255,34 +145,79 @@ static Stamp toStamp(Collection extends SecretSummary> secretSummaries, Set
return new Stamp(Set.copyOf(eTags), earliestExpiration == null ? now() : earliestExpiration);
}
+ static boolean isModified(Stamp pollStamp, Stamp stamp) {
+ return
+ !pollStamp.eTags().equals(stamp.eTags())
+ || stamp.earliestExpiration().isBefore(pollStamp.earliestExpiration());
+ }
+
+ static Callable task(BiConsumer valueNodes,
+ Consumer eTags,
+ String secretName,
+ Function f,
+ String secretId,
+ Base64.Decoder base64Decoder) {
+ return () -> {
+ valueNodes.accept(secretName,
+ valueNode(request -> secretBundleContentDetails(request, f, eTags),
+ secretId,
+ base64Decoder));
+ return null; // Callable; null is the only possible return value
+ };
+ }
+
/**
- * Creates and returns a new {@link Builder} for {@linkplain Builder#build() building} {@link
- * SecretBundleNodeConfigSource} instances.
+ * Returns {@code true} if the values in this {@link SecretBundleNodeConfigSource} have been modified.
*
- * @return a new {@link Builder}
+ * @param lastKnownStamp a {@link Stamp}
+ * @return {@code true} if modified
*/
- public static Builder builder() {
- return new Builder();
+ @Deprecated // For use by the Helidon Config subsystem only.
+ @Override // PollableSource
+ public boolean isModified(Stamp lastKnownStamp) {
+ Stamp stamp = this.stamper.get();
+ if (!stamp.eTags().equals(lastKnownStamp.eTags())) {
+ return true;
+ }
+ return stamp.earliestExpiration().isBefore(lastKnownStamp.earliestExpiration());
}
- private static void closeUnchecked(AutoCloseable autoCloseable) {
- try {
- autoCloseable.close();
- } catch (RuntimeException e) {
- throw e;
- } catch (InterruptedException e) {
- // (Can legally be thrown by any AutoCloseable. Must preserve interrupt status.)
- Thread.currentThread().interrupt();
- throw new IllegalStateException(e.getMessage(), e);
- } catch (Exception e) {
- // (Can legally be thrown by any AutoCloseable.)
- throw new IllegalStateException(e.getMessage(), e);
+ @Deprecated // For use by the Helidon Config subsystem only.
+ @Override // NodeConfigSource
+ public Optional load() {
+ return this.loader.get();
+ }
+
+ @Deprecated // For use by the Helidon Config subsystem only.
+ @Override // PollableSource
+ public Optional pollingStrategy() {
+ return super.pollingStrategy();
+ }
+
+ private static Stamp toStamp(Collection secretSummaries,
+ Supplier extends Secrets> secretsSupplier) {
+ if (secretSummaries.isEmpty()) {
+ return new Stamp();
}
+ Set eTags = ConcurrentHashMap.newKeySet();
+ Collection> tasks = new ArrayList<>(secretSummaries.size());
+ Secrets secrets = secretsSupplier.get();
+ for (SecretSummary ss : secretSummaries) {
+ if (ss.getLifecycleState() == LifecycleState.Active) {
+ tasks.add(() -> {
+ GetSecretBundleResponse response = secrets.getSecretBundle(request(ss.getId()));
+ eTags.add(response.getEtag());
+ return null; // Callable; null is the only possible return value
+ });
+ }
+ }
+ completeTasks(tasks, secrets);
+ return toStamp(secretSummaries, eTags);
}
- private static void completeTasks(Collection extends Callable> tasks, AutoCloseable autoCloseable) {
+ private static void completeTasks(Collection> tasks, AutoCloseable autoCloseable) {
try (ExecutorService es = newVirtualThreadPerTaskExecutor();
- autoCloseable) {
+ autoCloseable) {
completeTasks(es, tasks);
} catch (RuntimeException e) {
throw e;
@@ -296,7 +231,7 @@ private static void completeTasks(Collection extends Callable> tasks, Au
}
}
- private static void completeTasks(ExecutorService es, Collection extends Callable> tasks) {
+ private static void completeTasks(ExecutorService es, Collection> tasks) {
RuntimeException re = null;
for (Future> future : invokeAllUnchecked(es, tasks)) {
try {
@@ -325,7 +260,7 @@ private static T futureGetUnchecked(Future future) {
}
}
- private static List> invokeAllUnchecked(ExecutorService es, Collection extends Callable> tasks) {
+ private static List> invokeAllUnchecked(ExecutorService es, Collection> tasks) {
try {
return es.invokeAll(tasks);
} catch (InterruptedException e) {
@@ -334,12 +269,6 @@ private static List> invokeAllUnchecked(ExecutorService es, Collec
}
}
- static boolean isModified(Stamp pollStamp, Stamp stamp) {
- return
- !pollStamp.eTags().equals(stamp.eTags())
- || stamp.earliestExpiration().isBefore(pollStamp.earliestExpiration());
- }
-
private static GetSecretBundleRequest request(String secretId) {
return GetSecretBundleRequest.builder().secretId(secretId).build();
}
@@ -347,8 +276,8 @@ private static GetSecretBundleRequest request(String secretId) {
// Suppress "[try] auto-closeable resource Vaults has a member method close() that could throw
// InterruptedException" since we handle it.
@SuppressWarnings("try")
- private static Collection extends SecretSummary> secretSummaries(Supplier extends Vaults> vaultsSupplier,
- ListSecretsRequest listSecretsRequest) {
+ private static Collection secretSummaries(Supplier extends Vaults> vaultsSupplier,
+ ListSecretsRequest listSecretsRequest) {
try (Vaults v = vaultsSupplier.get()) {
return v.listSecrets(listSecretsRequest).getItems();
} catch (RuntimeException e) {
@@ -363,31 +292,16 @@ private static Collection extends SecretSummary> secretSummaries(Supplier ex
}
}
- static Callable task(BiConsumer super String, ? super ValueNode> valueNodes,
- Consumer super String> eTags,
- String secretName,
- Function super GetSecretBundleRequest, ? extends GetSecretBundleResponse> f,
- String secretId,
- Base64.Decoder base64Decoder) {
- return () -> {
- valueNodes.accept(secretName,
- valueNode(request -> secretBundleContentDetails(request, f, eTags),
- secretId,
- base64Decoder));
- return null; // Callable; null is the only possible return value
- };
- }
-
private static Base64SecretBundleContentDetails
- secretBundleContentDetails(GetSecretBundleRequest request,
- Function super GetSecretBundleRequest, ? extends GetSecretBundleResponse> f,
- Consumer super String> eTags) {
+ secretBundleContentDetails(GetSecretBundleRequest request,
+ Function f,
+ Consumer eTags) {
GetSecretBundleResponse response = f.apply(request);
eTags.accept(response.getEtag());
return (Base64SecretBundleContentDetails) response.getSecretBundle().getSecretBundleContent();
}
- private static ValueNode valueNode(Function super GetSecretBundleRequest, ? extends Base64SecretBundleContentDetails> f,
+ private static ValueNode valueNode(Function f,
String secretId,
Base64.Decoder base64Decoder) {
return valueNode(f.apply(request(secretId)), base64Decoder);
@@ -397,46 +311,57 @@ private static ValueNode valueNode(Base64SecretBundleContentDetails details, Bas
return valueNode(details.getContent(), base64Decoder);
}
+ private Optional load(Supplier extends Vaults> vaultsSupplier,
+ Supplier extends Secrets> secretsSupplier,
+ ListSecretsRequest listSecretsRequest) {
+ Collection secretSummaries = secretSummaries(vaultsSupplier, listSecretsRequest);
+ return this.load(secretSummaries, secretsSupplier);
+ }
- /*
- * Inner and nested classes.
- */
-
+ private Optional load(Collection secretSummaries,
+ Supplier extends Secrets> secretsSupplier) {
+ if (secretSummaries.isEmpty()) {
+ return Optional.empty();
+ }
+ ConcurrentMap valueNodes = new ConcurrentHashMap<>();
+ Set eTags = ConcurrentHashMap.newKeySet();
+ Collection> tasks = new ArrayList<>();
+ Base64.Decoder decoder = Base64.getDecoder();
+ Secrets secrets = secretsSupplier.get();
+ for (SecretSummary ss : secretSummaries) {
+ tasks.add(task(valueNodes::put,
+ eTags::add,
+ ss.getSecretName(),
+ secrets::getSecretBundle,
+ ss.getId(),
+ decoder));
+ }
+ completeTasks(tasks, secrets);
+ ObjectNode.Builder objectNodeBuilder = ObjectNode.builder();
+ for (Entry e : valueNodes.entrySet()) {
+ objectNodeBuilder.addValue(e.getKey(), e.getValue());
+ }
+ return Optional.of(NodeContent.builder()
+ .node(objectNodeBuilder.build())
+ .stamp(toStamp(secretSummaries, eTags))
+ .build());
+ }
/**
* An {@link AbstractConfigSourceBuilder} that {@linkplain #build() builds} {@link SecretBundleNodeConfigSource}
* instances.
*/
- // public static final class Builder extends AbstractConfigSourceBuilder {
public static final class Builder extends AbstractSecretBundleConfigSource.Builder {
- /*
- * Instance fields.
- */
-
-
private String compartmentOcid;
-
private Supplier extends Vaults> vaultsSupplier;
-
- /*
- * Constructors.
- */
-
-
private Builder() {
super();
VaultsClient.Builder vcb = VaultsClient.builder();
this.vaultsSupplier = () -> vcb.build(adpSupplier().get());
}
-
- /*
- * Instance methods.
- */
-
-
/**
* Creates and returns a new {@link SecretBundleNodeConfigSource} instance initialized from the state of this
* {@link Builder}.
@@ -452,10 +377,8 @@ public SecretBundleNodeConfigSource build() {
* SecretBundleNodeConfigSource} will retrieve values.
*
* @param compartmentOcid a valid OCID identifying an OCI compartment; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code compartmentId} is {@code null}
+ * @throws NullPointerException if {@code compartmentId} is {@code null}
*/
public Builder compartmentOcid(String compartmentOcid) {
this.compartmentOcid = Objects.requireNonNull(compartmentOcid, "compartmentOcid");
@@ -466,25 +389,24 @@ public Builder compartmentOcid(String compartmentOcid) {
* Configures this {@link Builder} from the supplied meta-configuration.
*
* @param metaConfig the meta-configuration; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code metaConfig} is {@code null}
+ * @throws NullPointerException if {@code metaConfig} is {@code null}
*/
@Override // AbstractConfigSourceBuilder
public Builder config(Config metaConfig) {
metaConfig.get("compartment-ocid")
- .asString()
- .filter(Predicate.not(String::isBlank))
- .ifPresentOrElse(this::compartmentOcid,
- () -> {
- if (LOGGER.isLoggable(WARNING)) {
- LOGGER.log(WARNING,
- "No meta-configuration value supplied for "
- + metaConfig.key().toString() + "." + COMPARTMENT_OCID_PROPERTY_NAME
- + "); resulting ConfigSource will be empty");
- }
- });
+ .asString()
+ .filter(Predicate.not(String::isBlank))
+ .ifPresentOrElse(this::compartmentOcid,
+ () -> {
+ if (LOGGER.isLoggable(WARNING)) {
+ LOGGER.log(WARNING,
+ "No meta-configuration value supplied for "
+ + metaConfig.key()
+ .toString() + "." + COMPARTMENT_OCID_PROPERTY_NAME
+ + "); resulting ConfigSource will be empty");
+ }
+ });
return super.config(metaConfig);
}
@@ -498,13 +420,9 @@ public Builder config(Config metaConfig) {
* super.pollingStrategy(pollingStrategy)} and returns the result.
*
* @param pollingStrategy a {@link PollingStrategy}; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code pollingStrategy} is {@code null}
- *
+ * @throws NullPointerException if {@code pollingStrategy} is {@code null}
* @see PollableSource
- *
* @see PollingStrategy
*/
public Builder pollingStrategy(PollingStrategy pollingStrategy) {
@@ -516,10 +434,8 @@ public Builder pollingStrategy(PollingStrategy pollingStrategy) {
* communicating with the OCI Vaults API.
*
* @param vaultsSupplier the non-default {@link Supplier} to use; must not be {@code null}
- *
* @return this {@link Builder}
- *
- * @exception NullPointerException if {@code vaultsSupplier} is {@code null}
+ * @throws NullPointerException if {@code vaultsSupplier} is {@code null}
*/
public Builder vaultsSupplier(Supplier extends Vaults> vaultsSupplier) {
this.vaultsSupplier = Objects.requireNonNull(vaultsSupplier, "vaultsSupplier");
@@ -532,10 +448,9 @@ public Builder vaultsSupplier(Supplier extends Vaults> vaultsSupplier) {
* A pairing of a {@link Set} of entity tags with an {@link Instant} identifying the earliest expiration
* of a Secret indirectly identified by one of those tags.
*
- * @param eTags a {@link Set} of entity tags
- *
+ * @param eTags a {@link Set} of entity tags
* @param earliestExpiration an {@link Instant} identifying the earliest expiration of a Secret indirectly
- * identified by one of the supplied tags
+ * identified by one of the supplied tags
*/
public record Stamp(Set> eTags, Instant earliestExpiration) {
@@ -549,7 +464,7 @@ public Stamp() {
/**
* Creates a new {@link Stamp}.
*
- * @exception NullPointerException if any argument is {@code null}
+ * @throws NullPointerException if any argument is {@code null}
*/
public Stamp {
eTags = Set.copyOf(Objects.requireNonNull(eTags, "eTags"));
diff --git a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java
similarity index 95%
rename from integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java
rename to integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java
index a6da275765c..86b33bab344 100644
--- a/integrations/oci/oci-secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java
+++ b/integrations/oci/secrets-config-source/src/main/java/io/helidon/integrations/oci/secrets/configsource/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
diff --git a/integrations/oci/oci-secrets-config-source/src/main/java/module-info.java b/integrations/oci/secrets-config-source/src/main/java/module-info.java
similarity index 83%
rename from integrations/oci/oci-secrets-config-source/src/main/java/module-info.java
rename to integrations/oci/secrets-config-source/src/main/java/module-info.java
index ef604eda5dd..1b9c5e785b7 100644
--- a/integrations/oci/oci-secrets-config-source/src/main/java/module-info.java
+++ b/integrations/oci/secrets-config-source/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -30,11 +30,13 @@
requires io.helidon.common;
requires transitive io.helidon.config;
- requires io.helidon.integrations.oci.sdk.runtime;
+ requires io.helidon.integrations.oci;
+ requires io.helidon.service.registry;
requires oci.java.sdk.common;
requires transitive oci.java.sdk.secrets;
requires transitive oci.java.sdk.vault;
- provides io.helidon.config.spi.ConfigSourceProvider with io.helidon.integrations.oci.secrets.configsource.OciSecretsConfigSourceProvider;
+ provides io.helidon.config.spi.ConfigSourceProvider
+ with io.helidon.integrations.oci.secrets.configsource.OciSecretsConfigSourceProvider;
}
diff --git a/integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java b/integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java
similarity index 97%
rename from integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java
rename to integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java
index 63b3045ad1b..bc7257355e5 100644
--- a/integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java
+++ b/integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/IsModifiedTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
diff --git a/integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java b/integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java
similarity index 97%
rename from integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java
rename to integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java
index 58b6ca856db..b8c7c4dad47 100644
--- a/integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java
+++ b/integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/UsageTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -23,7 +23,6 @@
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
diff --git a/integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java b/integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java
similarity index 96%
rename from integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java
rename to integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java
index c22640747b5..1b03f499328 100644
--- a/integrations/oci/oci-secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java
+++ b/integrations/oci/secrets-config-source/src/test/java/io/helidon/integrations/oci/secrets/configsource/ValueNodeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
diff --git a/integrations/oci/oci-secrets-config-source/src/test/java/logging.properties b/integrations/oci/secrets-config-source/src/test/java/logging.properties
similarity index 84%
rename from integrations/oci/oci-secrets-config-source/src/test/java/logging.properties
rename to integrations/oci/secrets-config-source/src/test/java/logging.properties
index 686736250f3..e86fc921f08 100644
--- a/integrations/oci/oci-secrets-config-source/src/test/java/logging.properties
+++ b/integrations/oci/secrets-config-source/src/test/java/logging.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
@@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-com.oracle.bmc.level = SEVERE
+
handlers = java.util.logging.ConsoleHandler
-io.helidon.integrations.oci.secrets.configsource.level = FINER
java.util.logging.ConsoleHandler.level = FINER
+
+com.oracle.bmc.level = SEVERE
+io.helidon.integrations.oci.secrets.configsource.level = INFO
\ No newline at end of file
diff --git a/integrations/oci/oci-secrets-config-source/src/test/resources/meta-config.yaml b/integrations/oci/secrets-config-source/src/test/resources/meta-config.yaml
similarity index 90%
rename from integrations/oci/oci-secrets-config-source/src/test/resources/meta-config.yaml
rename to integrations/oci/secrets-config-source/src/test/resources/meta-config.yaml
index 46c8243e420..78c62e11776 100644
--- a/integrations/oci/oci-secrets-config-source/src/test/resources/meta-config.yaml
+++ b/integrations/oci/secrets-config-source/src/test/resources/meta-config.yaml
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
@@ -17,6 +17,7 @@ sources:
- type: 'system-properties' # for testing
- type: 'oci-secrets'
properties: # required
+ optional: true
accept-pattern: '^FrancqueSecret$'
compartment-ocid: '${compartment-ocid}'
lazy: ${lazy}
diff --git a/integrations/oci/oci-secrets-mp-config-source/pom.xml b/integrations/oci/secrets-mp-config-source/pom.xml
similarity index 93%
rename from integrations/oci/oci-secrets-mp-config-source/pom.xml
rename to integrations/oci/secrets-mp-config-source/pom.xml
index 1ad4bba0449..1ae3ad4c2b0 100644
--- a/integrations/oci/oci-secrets-mp-config-source/pom.xml
+++ b/integrations/oci/secrets-mp-config-source/pom.xml
@@ -27,7 +27,7 @@
Helidon Integrations OCI Secrets MP Config Source
- OCI Secrets Retrieval API MPMetaConfigProvider Implementation
+ OCI Secrets Retrieval API MpMetaConfigProvider Implementation
@@ -59,7 +59,11 @@
-
+
+ io.helidon.config
+ helidon-config-yaml-mp
+ test
+ org.hamcresthamcrest-core
diff --git a/integrations/oci/oci-secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java b/integrations/oci/secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java
similarity index 99%
rename from integrations/oci/oci-secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java
rename to integrations/oci/secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java
index 6fa141af55e..a35d04b4b7b 100644
--- a/integrations/oci/oci-secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java
+++ b/integrations/oci/secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/OciSecretsMpMetaConfigProvider.java
@@ -49,7 +49,6 @@ public final class OciSecretsMpMetaConfigProvider implements MpMetaConfigProvide
* @deprecated For use by the Helidon Config subsystem only.
*/
@Deprecated // For java.util.ServiceLoader use only.
- @SuppressWarnings("deprecation")
public OciSecretsMpMetaConfigProvider() {
super();
this.p = new OciSecretsConfigSourceProvider();
diff --git a/integrations/oci/oci-secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java b/integrations/oci/secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java
similarity index 95%
rename from integrations/oci/oci-secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java
rename to integrations/oci/secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java
index 23d08c1da50..2a5871025c0 100644
--- a/integrations/oci/oci-secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java
+++ b/integrations/oci/secrets-mp-config-source/src/main/java/io/helidon/integrations/oci/secrets/mp/configsource/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
diff --git a/integrations/oci/oci-secrets-mp-config-source/src/main/java/module-info.java b/integrations/oci/secrets-mp-config-source/src/main/java/module-info.java
similarity index 96%
rename from integrations/oci/oci-secrets-mp-config-source/src/main/java/module-info.java
rename to integrations/oci/secrets-mp-config-source/src/main/java/module-info.java
index c9573022abb..8d2d33827ab 100644
--- a/integrations/oci/oci-secrets-mp-config-source/src/main/java/module-info.java
+++ b/integrations/oci/secrets-mp-config-source/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
diff --git a/integrations/oci/oci-secrets-mp-config-source/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java b/integrations/oci/secrets-mp-config-source/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java
similarity index 97%
rename from integrations/oci/oci-secrets-mp-config-source/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java
rename to integrations/oci/secrets-mp-config-source/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java
index 2d8363ed652..26d3b1ae08f 100644
--- a/integrations/oci/oci-secrets-mp-config-source/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java
+++ b/integrations/oci/secrets-mp-config-source/src/test/java/io/helidon/integrations/oci/secrets/mp/configsource/UsageTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -23,7 +23,6 @@
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
diff --git a/integrations/oci/oci-secrets-mp-config-source/src/test/java/logging.properties b/integrations/oci/secrets-mp-config-source/src/test/java/logging.properties
similarity index 92%
rename from integrations/oci/oci-secrets-mp-config-source/src/test/java/logging.properties
rename to integrations/oci/secrets-mp-config-source/src/test/java/logging.properties
index 9587ed14579..2bc1929608e 100644
--- a/integrations/oci/oci-secrets-mp-config-source/src/test/java/logging.properties
+++ b/integrations/oci/secrets-mp-config-source/src/test/java/logging.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
diff --git a/integrations/oci/oci-secrets-mp-config-source/src/test/resources/mp-meta-config.yaml b/integrations/oci/secrets-mp-config-source/src/test/resources/mp-meta-config.yaml
similarity index 91%
rename from integrations/oci/oci-secrets-mp-config-source/src/test/resources/mp-meta-config.yaml
rename to integrations/oci/secrets-mp-config-source/src/test/resources/mp-meta-config.yaml
index 9fa225b8c36..0f285278017 100644
--- a/integrations/oci/oci-secrets-mp-config-source/src/test/resources/mp-meta-config.yaml
+++ b/integrations/oci/secrets-mp-config-source/src/test/resources/mp-meta-config.yaml
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
@@ -21,6 +21,7 @@ sources:
- type: 'environment-variables'
- type: 'system-properties'
- type: 'oci-secrets'
+ optional: true
accept-pattern: '^FrancqueSecret$'
compartment-ocid: '${compartment-ocid}'
lazy: ${lazy}
diff --git a/integrations/oci/tls-certificates/README.md b/integrations/oci/tls-certificates/README.md
index b6c19524c04..cda39e89feb 100644
--- a/integrations/oci/tls-certificates/README.md
+++ b/integrations/oci/tls-certificates/README.md
@@ -1,6 +1,9 @@
# Helidon Integrations for OCI Key Manager Service
-This module contains the **_OciKmsTlsManager_** provider that offers lifecycle and rotation of certificates to be used with Helidon Tls when configured. It is designed specifically to integrate with [Oracle Cloud Infrastructure](https://www.oracle.com/cloud)'s [Certificates](https://www.oracle.com/security/cloud-security/ssl-tls-certificates) Service.
+This module contains the **_OciKmsTlsManager_** provider that offers lifecycle and rotation of certificates to be used with Helidon TLS when configured. It is designed specifically to integrate with [Oracle Cloud Infrastructure](https://www.oracle.com/cloud)'s [Certificates](https://www.oracle.com/security/cloud-security/ssl-tls-certificates) Service.
+
+This module uses the global Service Registry, as it is discovered through service loader,
+and as such cannot get dependencies directly from registry.
## Usage
Integrating with OCI's Certificates Service from Helidon is a simple matter of configuration.
diff --git a/integrations/oci/tls-certificates/pom.xml b/integrations/oci/tls-certificates/pom.xml
index 4b64b407263..6846f6d98a7 100644
--- a/integrations/oci/tls-certificates/pom.xml
+++ b/integrations/oci/tls-certificates/pom.xml
@@ -55,25 +55,14 @@
com.oracle.oci.sdkoci-java-sdk-vault
-
- jakarta.inject
- jakarta.inject-api
- true
-
-
- jakarta.annotation
- jakarta.annotation-api
- true
-
-
- io.helidon.config
- helidon-config-metadata
- true
- io.helidon.builderhelidon-builder-api
+
+ io.helidon.service
+ helidon-service-registry
+ io.helidon.commonhelidon-common-tls
@@ -82,25 +71,17 @@
io.helidon.commonhelidon-common-key-util
-
- io.helidon.integrations.oci.sdk
- helidon-integrations-oci-sdk-runtime
- io.helidon.fault-tolerancehelidon-fault-tolerance
-
- io.helidon.config
- helidon-config-yaml
- io.helidon.schedulinghelidon-scheduling
- io.helidon.inject.configdriven
- helidon-inject-configdriven-runtime
+ io.helidon.integrations.oci
+ helidon-integrations-oci
@@ -109,7 +90,6 @@
oci-java-sdk-common-httpclient-jersey3test
-
io.helidon.common.testinghelidon-common-testing-junit5
@@ -126,21 +106,10 @@
test
- io.helidon.microprofile.cdi
- helidon-microprofile-cdi
- test
-
-
- io.helidon.microprofile.testing
- helidon-microprofile-testing-junit5
- test
-
-
- io.helidon.microprofile.bundles
- helidon-microprofile
+ io.helidon.config
+ helidon-config-yamltest
-
@@ -148,82 +117,59 @@
org.apache.maven.pluginsmaven-compiler-plugin
-
-
- default-testCompile
-
- testCompile
-
-
- true
-
- -Ainject.ignoreUnsupportedAnnotations=true
-
-
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
- ${helidon.version}
-
-
- io.helidon.builder
- helidon-builder-processor
- ${helidon.version}
-
-
- io.helidon.common.processor
- helidon-common-processor-helidon-copyright
- ${helidon.version}
-
-
- io.helidon.config
- helidon-config-metadata-processor
- ${helidon.version}
-
-
-
-
-
- default-compile
-
- compile
-
-
- true
-
-
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
- ${helidon.version}
-
-
- io.helidon.builder
- helidon-builder-processor
- ${helidon.version}
-
-
- io.helidon.common.processor
- helidon-common-processor-helidon-copyright
- ${helidon.version}
-
-
-
-
-
+
+
+
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+
+
+ io.helidon.builder
+ helidon-builder-codegen
+ ${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-codegen
+ ${helidon.version}
+
+
+ io.helidon.codegen
+ helidon-codegen-helidon-copyright
+ ${helidon.version}
+
+
+ io.helidon.config
+ helidon-config-metadata-processor
+ ${helidon.version}
+
+
+
+
+ io.helidon.codegen
+ helidon-codegen-apt
+ ${helidon.version}
+ io.helidon.builder
- helidon-builder-processor
+ helidon-builder-codegen
+ ${helidon.version}
+
+
+ io.helidon.service
+ helidon-service-codegen${helidon.version}
- io.helidon.inject.configdriven
- helidon-inject-configdriven-processor
+ io.helidon.codegen
+ helidon-codegen-helidon-copyright${helidon.version}
- io.helidon.common.processor
- helidon-common-processor-helidon-copyright
+ io.helidon.config
+ helidon-config-metadata-processor${helidon.version}
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesDownloader.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesDownloader.java
index 1c29ab89e3c..17995c8fbb0 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesDownloader.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -26,24 +26,32 @@
import java.util.List;
import java.util.Objects;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
import io.helidon.common.pki.PemReader;
-import io.helidon.integrations.oci.sdk.runtime.OciExtension;
import io.helidon.integrations.oci.tls.certificates.spi.OciCertificatesDownloader;
+import io.helidon.service.registry.Service;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
import com.oracle.bmc.certificates.CertificatesClient;
import com.oracle.bmc.certificates.requests.GetCertificateAuthorityBundleRequest;
import com.oracle.bmc.certificates.requests.GetCertificateBundleRequest;
import com.oracle.bmc.certificates.responses.GetCertificateAuthorityBundleResponse;
import com.oracle.bmc.certificates.responses.GetCertificateBundleResponse;
-import jakarta.inject.Singleton;
import static io.helidon.integrations.oci.tls.certificates.spi.OciCertificatesDownloader.create;
/**
* Implementation of the {@link OciCertificatesDownloader} that will use OCI's Certificates Service to download certs.
*/
-@Singleton
+@Service.Provider
+@Weight(Weighted.DEFAULT_WEIGHT - 10)
class DefaultOciCertificatesDownloader implements OciCertificatesDownloader {
+ private final AbstractAuthenticationDetailsProvider authProvider;
+
+ DefaultOciCertificatesDownloader(AbstractAuthenticationDetailsProvider authProvider) {
+ this.authProvider = authProvider;
+ }
@Override
public Certificates loadCertificates(String certOcid) {
@@ -65,9 +73,9 @@ public X509Certificate loadCACertificate(String caCertOcid) {
}
}
- static Certificates loadCerts(String certOcid) {
+ Certificates loadCerts(String certOcid) {
try (CertificatesClient client = CertificatesClient.builder()
- .build(OciExtension.ociAuthenticationProvider().get())) {
+ .build(authProvider)) {
GetCertificateBundleResponse res =
client.getCertificateBundle(GetCertificateBundleRequest.builder()
.certificateId(certOcid)
@@ -82,10 +90,10 @@ static Certificates loadCerts(String certOcid) {
}
}
- static X509Certificate loadCACert(String caCertOcid) {
+ X509Certificate loadCACert(String caCertOcid) {
GetCertificateAuthorityBundleResponse res;
try (CertificatesClient client = CertificatesClient.builder()
- .build(OciExtension.ociAuthenticationProvider().get())) {
+ .build(authProvider)) {
res = client.getCertificateAuthorityBundle(GetCertificateAuthorityBundleRequest.builder()
.certificateAuthorityId(caCertOcid)
.build());
@@ -109,7 +117,7 @@ static X509Certificate toCertificate(InputStream certIs) {
if (certs.size() != 1) {
throw new IllegalStateException("Expected a single certificate in stream but found: " + certs.size());
}
- return certs.get(0);
+ return certs.getFirst();
}
// use the eTag, defaulting to the hash of the certs if not present
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManager.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManager.java
index bf3fd52cf11..10a9440e363 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManager.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManager.java
@@ -27,6 +27,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
@@ -36,14 +37,10 @@
import io.helidon.common.tls.ConfiguredTlsManager;
import io.helidon.common.tls.TlsConfig;
import io.helidon.config.Config;
-import io.helidon.faulttolerance.Async;
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.ServiceProvider;
-import io.helidon.inject.api.Services;
import io.helidon.integrations.oci.tls.certificates.spi.OciCertificatesDownloader;
import io.helidon.integrations.oci.tls.certificates.spi.OciPrivateKeyDownloader;
-
-import jakarta.inject.Provider;
+import io.helidon.service.registry.GlobalServiceRegistry;
+import io.helidon.service.registry.ServiceRegistry;
/**
* The default implementation (service loader and provider-driven) implementation of {@link OciCertificatesTlsManager}.
@@ -57,11 +54,8 @@ class DefaultOciCertificatesTlsManager extends ConfiguredTlsManager implements O
private final OciCertificatesTlsManagerConfig cfg;
private final AtomicReference lastVersionDownloaded = new AtomicReference<>("");
- // these will only be non-null when enabled
- private Provider pkDownloader;
- private Provider certDownloader;
- private ScheduledExecutorService asyncExecutor;
- private Async async;
+ private Supplier pkDownloader;
+ private Supplier certDownloader;
private TlsConfig tlsConfig;
DefaultOciCertificatesTlsManager(OciCertificatesTlsManagerConfig cfg) {
@@ -83,37 +77,26 @@ class DefaultOciCertificatesTlsManager extends ConfiguredTlsManager implements O
@Override // TlsManager
public void init(TlsConfig tls) {
this.tlsConfig = tls;
- Services services = InjectionServices.realizedServices();
- this.pkDownloader = services.lookupFirst(OciPrivateKeyDownloader.class);
- this.certDownloader = services.lookupFirst(OciCertificatesDownloader.class);
- this.asyncExecutor = Executors.newSingleThreadScheduledExecutor();
- this.async = Async.builder().executor(asyncExecutor).build();
+ ServiceRegistry registry = GlobalServiceRegistry.registry();
+ this.pkDownloader = registry.supply(OciPrivateKeyDownloader.class);
+ this.certDownloader = registry.supply(OciCertificatesDownloader.class);
+ ScheduledExecutorService asyncExecutor = Executors.newSingleThreadScheduledExecutor();
// the initial loading of the tls
loadContext(true);
- // register for any available graceful shutdown events
- Optional> shutdownHook = services.lookupFirst(LifecycleHook.class, false);
- shutdownHook.ifPresent(sp -> sp.get().registerShutdownConsumer(this::shutdown));
-
// now schedule for reload checking
String taskIntervalDescription =
- io.helidon.scheduling.Scheduling.cronBuilder()
+ io.helidon.scheduling.Scheduling.cron()
.executor(asyncExecutor)
.expression(cfg.schedule())
.task(inv -> maybeReload())
.build()
.description();
- LOGGER.log(System.Logger.Level.DEBUG, () ->
- OciCertificatesTlsManagerConfig.class.getSimpleName() + " scheduled: " + taskIntervalDescription);
- }
- private void shutdown(Object event) {
- try {
- LOGGER.log(System.Logger.Level.DEBUG, "Shutting down");
- asyncExecutor.shutdownNow();
- } catch (Exception e) {
- LOGGER.log(System.Logger.Level.WARNING, "Shut down failed", e);
+ if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
+ LOGGER.log(System.Logger.Level.DEBUG,
+ "Scheduled: " + taskIntervalDescription);
}
}
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java
index 593824375da..6ae89793450 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -23,7 +23,6 @@
/**
* The service provider for {@link OciCertificatesTlsManager}.
*/
-//@Singleton - this is config driven, not pico driven - need to rectify this
public class DefaultOciCertificatesTlsManagerProvider implements TlsManagerProvider {
/**
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciPrivateKeyDownloader.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciPrivateKeyDownloader.java
index 3c3b08d4003..99d68e2a7c5 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciPrivateKeyDownloader.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciPrivateKeyDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -34,24 +34,26 @@
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
-import io.helidon.integrations.oci.sdk.runtime.OciExtension;
import io.helidon.integrations.oci.tls.certificates.spi.OciPrivateKeyDownloader;
+import io.helidon.service.registry.Service;
+import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
import com.oracle.bmc.keymanagement.KmsCryptoClient;
import com.oracle.bmc.keymanagement.model.ExportKeyDetails;
import com.oracle.bmc.keymanagement.requests.ExportKeyRequest;
import com.oracle.bmc.keymanagement.responses.ExportKeyResponse;
-import jakarta.inject.Singleton;
/**
* Implementation of the {@link OciPrivateKeyDownloader} that will use OCI's KMS to export a key.
*/
-@Singleton
+@Service.Provider
class DefaultOciPrivateKeyDownloader implements OciPrivateKeyDownloader {
private final PrivateKey wrappingPrivateKey;
private final String wrappingPublicKeyPem;
+ private final AbstractAuthenticationDetailsProvider authProvider;
- DefaultOciPrivateKeyDownloader() {
+ DefaultOciPrivateKeyDownloader(AbstractAuthenticationDetailsProvider authProvider) {
+ this.authProvider = authProvider;
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
@@ -72,7 +74,7 @@ public PrivateKey loadKey(String keyOcid,
Objects.requireNonNull(vaultCryptoEndpoint);
try (KmsCryptoClient client = KmsCryptoClient.builder()
.endpoint(vaultCryptoEndpoint.toString())
- .build(OciExtension.ociAuthenticationProvider().get())) {
+ .build(authProvider)) {
ExportKeyResponse exportKeyResponse =
client.exportKey(ExportKeyRequest.builder()
.exportKeyDetails(ExportKeyDetails.builder()
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/LifecycleHook.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/LifecycleHook.java
deleted file mode 100644
index 9dc6f078242..00000000000
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/LifecycleHook.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.integrations.oci.tls.certificates;
-
-import java.util.function.Consumer;
-
-import io.helidon.inject.api.Contract;
-
-// consider making this public and relocating this to somewhere under common or inject
-// it is here to ensure proper shutdown (decoupled from mp) and presenting non-intermittent test failures
-@Contract
-interface LifecycleHook {
-
- void registerStartupConsumer(Consumer consumer);
-
- void registerShutdownConsumer(Consumer consumer);
-
-}
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerConfigBlueprint.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerConfigBlueprint.java
index c9393f6f1c9..63c781e6516 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerConfigBlueprint.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerConfigBlueprint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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,15 +20,14 @@
import java.util.Optional;
import java.util.function.Supplier;
+import io.helidon.builder.api.Option;
import io.helidon.builder.api.Prototype;
-import io.helidon.config.metadata.Configured;
-import io.helidon.config.metadata.ConfiguredOption;
/**
* Blueprint configuration for {@link OciCertificatesTlsManager}.
*/
@Prototype.Blueprint
-@Configured
+@Prototype.Configured
interface OciCertificatesTlsManagerConfigBlueprint extends Prototype.Factory {
/**
@@ -37,7 +36,7 @@ interface OciCertificatesTlsManagerConfigBlueprint extends Prototype.Factory vaultManagementEndpoint();
/**
@@ -64,7 +63,7 @@ interface OciCertificatesTlsManagerConfigBlueprint extends Prototype.Factory compartmentOcid();
/**
@@ -72,7 +71,7 @@ interface OciCertificatesTlsManagerConfigBlueprint extends Prototype.Factory keyPassword();
}
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciCertificatesDownloader.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciCertificatesDownloader.java
index b47e62b8f58..6cccafc8e57 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciCertificatesDownloader.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciCertificatesDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -19,12 +19,12 @@
import java.security.cert.X509Certificate;
import java.util.Objects;
-import io.helidon.inject.api.Contract;
+import io.helidon.service.registry.Service;
/**
* The contract used for downloading certificates from OCI.
*/
-@Contract
+@Service.Contract
public interface OciCertificatesDownloader {
/**
diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciPrivateKeyDownloader.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciPrivateKeyDownloader.java
index 5e28d389a06..809f558fff9 100644
--- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciPrivateKeyDownloader.java
+++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/spi/OciPrivateKeyDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -19,12 +19,12 @@
import java.net.URI;
import java.security.PrivateKey;
-import io.helidon.inject.api.Contract;
+import io.helidon.service.registry.Service;
/**
* The contract used for downloading private keys from OCI.
*/
-@Contract
+@Service.Contract
public interface OciPrivateKeyDownloader {
/**
diff --git a/integrations/oci/tls-certificates/src/main/java/module-info.java b/integrations/oci/tls-certificates/src/main/java/module-info.java
index c57615445be..42f7b8cc4e2 100644
--- a/integrations/oci/tls-certificates/src/main/java/module-info.java
+++ b/integrations/oci/tls-certificates/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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,10 +18,6 @@
* Helidon Integrations of OCI Certificates Service.
*/
module io.helidon.integrations.oci.tls.certificates {
- requires static io.helidon.config.metadata;
- requires static jakarta.annotation;
- requires static jakarta.inject;
-
requires io.helidon.builder.api;
requires io.helidon.common;
requires io.helidon.common.config;
@@ -29,10 +25,9 @@
requires io.helidon.common.tls;
requires io.helidon.config;
requires io.helidon.faulttolerance;
- requires io.helidon.integrations.oci.sdk.runtime;
- requires io.helidon.inject.api;
- requires io.helidon.inject.runtime;
requires io.helidon.scheduling;
+ requires io.helidon.service.registry;
+ requires io.helidon.integrations.oci;
requires oci.java.sdk.common;
requires oci.java.sdk.certificates;
@@ -47,6 +42,4 @@
provides io.helidon.common.tls.spi.TlsManagerProvider
with io.helidon.integrations.oci.tls.certificates.DefaultOciCertificatesTlsManagerProvider;
- provides io.helidon.inject.api.ModuleComponent
- with io.helidon.integrations.oci.tls.certificates.Injection$$Module;
}
diff --git a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerTest.java b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerTest.java
index cb889262814..88ee911e7c7 100644
--- a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerTest.java
+++ b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManagerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -17,15 +17,10 @@
package io.helidon.integrations.oci.tls.certificates;
import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import io.helidon.common.tls.Tls;
import io.helidon.config.Config;
import io.helidon.config.ConfigSources;
-import io.helidon.inject.api.InjectionServices;
-import io.helidon.inject.api.Services;
-import io.helidon.microprofile.server.Server;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@@ -43,42 +38,6 @@ void reset() {
TestOciCertificatesDownloader.callCount_loadCertificates = 0;
TestOciCertificatesDownloader.callCount_loadCACertificate = 0;
TestOciPrivateKeyDownloader.callCount = 0;
- TestingCdiExtension.shutdownCalled = false;
- }
-
- @Test
- void serverRuntime() throws Exception {
- Services services = InjectionServices.realizedServices();
- LifecycleHook lifecycleHook = services.lookupFirst(LifecycleHook.class).get();
- CountDownLatch startup = new CountDownLatch(1);
-
- lifecycleHook.registerStartupConsumer(c -> startup.countDown());
- Server server = startServer();
- boolean res = startup.await(10, TimeUnit.SECONDS);
- assertThat(res,
- is(true));
-
- CountDownLatch shutdown = new CountDownLatch(1);
- lifecycleHook.registerShutdownConsumer(c -> shutdown.countDown());
-
- int certDownloadCountBaseline = TestOciCertificatesDownloader.callCount_loadCertificates;
- int caCertDownloadCountBaseline = TestOciCertificatesDownloader.callCount_loadCACertificate;
- int pkDownloadCountBaseLine = TestOciPrivateKeyDownloader.callCount;
- try {
- assertThat(certDownloadCountBaseline,
- equalTo(1));
- assertThat(caCertDownloadCountBaseline,
- equalTo(1));
- assertThat(pkDownloadCountBaseLine,
- equalTo(1));
- } finally {
- server.stop();
- res = shutdown.await(10, TimeUnit.SECONDS);
- assertThat(res,
- is(true));
- }
-
- assertThat(TestingCdiExtension.shutdownCalled, is(true));
}
@Test
@@ -143,9 +102,4 @@ void configIsMonitoredForChange() throws Exception {
.get("server.sockets.0.tls.manager.oci-certificates-tls-manager.key-password").asString().asOptional(),
is(Optional.of("changed")));
}
-
- static Server startServer() {
- return Server.create().start();
- }
-
}
diff --git a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciCertificatesDownloader.java b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciCertificatesDownloader.java
index fe5dd448ddc..bbabfd7205a 100644
--- a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciCertificatesDownloader.java
+++ b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciCertificatesDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -22,20 +22,26 @@
import java.security.cert.X509Certificate;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import io.helidon.common.Weight;
import io.helidon.common.Weighted;
import io.helidon.integrations.oci.tls.certificates.spi.OciCertificatesDownloader;
+import io.helidon.service.registry.Service;
-import jakarta.inject.Singleton;
-
-@Singleton
+@Service.Provider
@Weight(Weighted.DEFAULT_WEIGHT + 1)
-class TestOciCertificatesDownloader extends DefaultOciCertificatesDownloader {
+class TestOciCertificatesDownloader implements OciCertificatesDownloader {
static String version = "1";
- static int callCount_loadCertificates;
- static int callCount_loadCACertificate;
+ static volatile int callCount_loadCertificates;
+ static volatile int callCount_loadCACertificate;
+
+ private final Supplier realDownloader;
+
+ TestOciCertificatesDownloader(Supplier realDownloader) {
+ this.realDownloader = realDownloader;
+ }
@Override
public Certificates loadCertificates(String certOcid) {
@@ -43,13 +49,13 @@ public Certificates loadCertificates(String certOcid) {
try {
if (OciTestUtils.ociRealUsage()) {
- return super.loadCertificates(certOcid);
+ return realDownloader.get().loadCertificates(certOcid);
} else {
TimeUnit.MILLISECONDS.sleep(1); // make sure metrics timestamp changes
Objects.requireNonNull(certOcid);
try (InputStream certIs =
TestOciCertificatesDownloader.class.getClassLoader().getResourceAsStream("test-keys/serverCert.pem")) {
- return OciCertificatesDownloader.create(version, new X509Certificate[] {toCertificate(certIs)});
+ return OciCertificatesDownloader.create(version, new X509Certificate[] {DefaultOciCertificatesDownloader.toCertificate(certIs)});
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -66,13 +72,13 @@ public X509Certificate loadCACertificate(String caCertOcid) {
try {
if (OciTestUtils.ociRealUsage()) {
- return super.loadCACertificate(caCertOcid);
+ return realDownloader.get().loadCACertificate(caCertOcid);
} else {
TimeUnit.MILLISECONDS.sleep(1); // make sure metrics timestamp changes
Objects.requireNonNull(caCertOcid);
try (InputStream caCertIs =
TestOciCertificatesDownloader.class.getClassLoader().getResourceAsStream("test-keys/ca.pem")) {
- return toCertificate(caCertIs);
+ return DefaultOciCertificatesDownloader.toCertificate(caCertIs);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciPrivateKeyDownloader.java b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciPrivateKeyDownloader.java
index a0383ac4438..e15fc2e1df8 100644
--- a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciPrivateKeyDownloader.java
+++ b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestOciPrivateKeyDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -19,19 +19,26 @@
import java.net.URI;
import java.security.PrivateKey;
import java.util.Objects;
+import java.util.function.Supplier;
import io.helidon.common.Weight;
import io.helidon.common.Weighted;
import io.helidon.common.pki.Keys;
import io.helidon.config.Config;
+import io.helidon.integrations.oci.tls.certificates.spi.OciPrivateKeyDownloader;
+import io.helidon.service.registry.Service;
-import jakarta.inject.Singleton;
-
-@Singleton
+@Service.Provider
@Weight(Weighted.DEFAULT_WEIGHT + 1)
-class TestOciPrivateKeyDownloader extends DefaultOciPrivateKeyDownloader {
+class TestOciPrivateKeyDownloader implements OciPrivateKeyDownloader {
+
+ static volatile int callCount;
- static int callCount;
+ private final Supplier realDownloader;
+
+ TestOciPrivateKeyDownloader(Supplier realDownloader) {
+ this.realDownloader = realDownloader;
+ }
@Override
public PrivateKey loadKey(String keyOcid,
@@ -40,7 +47,7 @@ public PrivateKey loadKey(String keyOcid,
try {
if (OciTestUtils.ociRealUsage()) {
- return super.loadKey(keyOcid, vaultCryptoEndpoint);
+ return realDownloader.get().loadKey(keyOcid, vaultCryptoEndpoint);
} else {
Objects.requireNonNull(keyOcid);
Objects.requireNonNull(vaultCryptoEndpoint);
diff --git a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestingCdiExtension.java b/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestingCdiExtension.java
deleted file mode 100644
index 5f2d27751b4..00000000000
--- a/integrations/oci/tls-certificates/src/test/java/io/helidon/integrations/oci/tls/certificates/TestingCdiExtension.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.integrations.oci.tls.certificates;
-
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-import io.helidon.config.Config;
-import io.helidon.microprofile.cdi.RuntimeStart;
-
-import jakarta.annotation.Priority;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.context.BeforeDestroyed;
-import jakarta.enterprise.event.Observes;
-import jakarta.enterprise.inject.spi.Extension;
-import jakarta.inject.Singleton;
-
-import static jakarta.interceptor.Interceptor.Priority.PLATFORM_AFTER;
-
-@Singleton
-@SuppressWarnings("unused")
-// consider relocating this to somewhere under inject
-public class TestingCdiExtension implements Extension, LifecycleHook {
- static volatile boolean shutdownCalled;
- static final AtomicInteger running = new AtomicInteger();
- static final CopyOnWriteArrayList> startupConsumers = new CopyOnWriteArrayList<>();
- static final CopyOnWriteArrayList> shutdownConsumers = new CopyOnWriteArrayList<>();
-
- public TestingCdiExtension() {
- }
-
- void starting(@Observes @RuntimeStart Config config) {
- running.incrementAndGet();
- startupConsumers.forEach(c -> c.accept(config));
- startupConsumers.clear();
- shutdownCalled = false;
- }
-
- void stopping(@Observes @Priority(PLATFORM_AFTER) @BeforeDestroyed(ApplicationScoped.class) Object event) {
- running.decrementAndGet();
- shutdownConsumers.forEach(c -> c.accept(event));
- shutdownCalled = !shutdownConsumers.isEmpty();
- shutdownConsumers.clear();
- }
-
- @Override
- public void registerStartupConsumer(Consumer consumer) {
- startupConsumers.add(consumer);
- }
-
- @Override
- public void registerShutdownConsumer(Consumer consumer) {
- shutdownConsumers.add(consumer);
- }
-
-}
diff --git a/integrations/oci/tls-certificates/src/test/resources/oci.yaml b/integrations/oci/tls-certificates/src/test/resources/oci-config.yaml
similarity index 76%
rename from integrations/oci/tls-certificates/src/test/resources/oci.yaml
rename to integrations/oci/tls-certificates/src/test/resources/oci-config.yaml
index 9f2ca2ef77b..a0bc65a2b74 100644
--- a/integrations/oci/tls-certificates/src/test/resources/oci.yaml
+++ b/integrations/oci/tls-certificates/src/test/resources/oci-config.yaml
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Oracle and/or its affiliates.
+# Copyright (c) 2023, 2024 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.
@@ -14,4 +14,5 @@
# limitations under the License.
#
-auth-strategies: ["instance-principals", "config-file", "resource-principal", "config"]
+helidon.oci:
+ allowed-atn-strategies: ["instance-principals", "config-file", "resource-principal", "config"]
diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java
index 65eb64e4068..4e5d78cb76f 100644
--- a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java
+++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/BuildTimeInitializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2024 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.
@@ -34,7 +34,14 @@ final class BuildTimeInitializer {
static {
// need to initialize logging as soon as possible
LogConfig.initClass();
- createContainer();
+
+ try {
+ createContainer();
+ } catch (Throwable e) {
+ System.getLogger(BuildTimeInitializer.class.getName())
+ .log(System.Logger.Level.ERROR, "Failed to initialize CDI container", e);
+ throw e;
+ }
}
private BuildTimeInitializer() {
diff --git a/pom.xml b/pom.xml
index 293c7120b01..db3d6bfeb05 100644
--- a/pom.xml
+++ b/pom.xml
@@ -224,6 +224,7 @@
webserverwebsocketcodegen
+ service
diff --git a/service/README.md b/service/README.md
new file mode 100644
index 00000000000..806af08a5a7
--- /dev/null
+++ b/service/README.md
@@ -0,0 +1,93 @@
+Helidon Service Registry
+----
+
+# Core Service Registry
+
+Provides a replacement for Java ServiceLoader with basic inversion of control mechanism.
+Each service may have constructor parameters that expect instances of other services.
+
+The constructor dependency types can be as follows (`Contract` is used as the contract the service implements)
+
+- `Contract` - simply get an instance of another service
+- `Optional` - get an instance of another service, the other service may not be available
+- `List` - get instances of all services that are available
+- `Supplier`, `Supplier>`, `Supplier>` - equivalent methods but the value is resolved
+ when `Supplier.get()` is called, to allow more control
+
+Equivalent behavior can be achieved programmatically through `io.helidon.service.registry.ServiceRegistry` instance. This can be
+obtained from a `ServiceRegistryManager`.
+
+## Declare a service
+
+Use `io.helidon.service.registry.Service.Provider` annotation on your service provider type (implementation of a contract).
+Use `io.helidon.service.registry.Service.Contract` on your contract interface (if not annotated, such an interface would not be
+considered a contract and will not be discoverable using the registry - configurable).
+Use `io.helidon.service.registry.Service.ExternalContracts` on your service provider type to
+add other types as contracts, even if not annotated with `Contract` (i.e. to support third party libraries).
+
+Use `io.helidon.service.registry.Service.Descriptor` to create a hand-crafted service descriptor (see below "Behind the scenes")
+
+Service example:
+
+```java
+import io.helidon.service.registry.Service;
+
+@Service.Provider
+class MyService implements MyContract {
+ MyService() {
+ }
+
+ @Override
+ public String message() {
+ return "MyService";
+ }
+}
+```
+
+Service with dependency example:
+
+```java
+import io.helidon.service.registry.Service;
+
+@Service.Provider
+class MyService2 implements MyContract2 {
+ private final MyContract dependency;
+
+ MyService2(MyContract dependency) {
+ this.dependency = dependency;
+ }
+
+ @Override
+ public String message() {
+ return dependency.message();
+ }
+}
+```
+
+## Behind the scenes
+
+For each service, Helidon generates a service descriptor (`ServiceProvider__ServiceDescriptor`).
+This descriptor is discovered at runtime and used to instantiate a service without the need to use reflection.
+
+Reflection is used only to obtain an instance of the service descriptor (by using its public `INSTANCE` singleton field). As both
+the descriptor and the `INSTANCE` field are always public, there is no need to add `opens` to `module-info.java`.
+Support for GraalVM native image is handled in Helidon native image extension, by registering all service descriptors for
+reflection (the class, and the field).
+
+### Registry file format
+
+The service registry uses a `service.registry` in `META-INF/helidon` directory to store the main metadata of
+the service. This is to allow proper ordering of services (Service weight is one of the information stored) and
+lazy loading of services (which is the approach chosen in the core service registry).
+
+The format is as follows:
+
+```
+registry-type:service-descriptor-type:weight(double):contracts(comma separated)
+```
+
+Example:
+
+```
+core:io.helidon.ContractImpl__ServiceDescriptor:101.3:io.helidon.Contract1,io.helidon.Contract2
+```
\ No newline at end of file
diff --git a/service/codegen/pom.xml b/service/codegen/pom.xml
new file mode 100644
index 00000000000..ce6fe0e463a
--- /dev/null
+++ b/service/codegen/pom.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+ io.helidon.service
+ helidon-service-project
+ 4.0.0-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ helidon-service-codegen
+ Helidon Service Codegen
+
+ Code generation implementation for Helidon Service, to be used from annotation processing and from maven plugin
+
+
+
+
+ io.helidon.common
+ helidon-common-types
+
+
+ io.helidon.common
+ helidon-common
+
+
+ io.helidon.codegen
+ helidon-codegen
+
+
+ io.helidon.codegen
+ helidon-codegen-class-model
+
+
+
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCode.java b/service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCode.java
new file mode 100644
index 00000000000..d9e5e06f406
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCode.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 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.service.codegen;
+
+import java.util.Set;
+
+import io.helidon.codegen.ClassCode;
+import io.helidon.common.types.TypeName;
+
+/**
+ * New service descriptor metadata with its class code.
+ */
+interface DescriptorClassCode {
+ /**
+ * New source code information.
+ *
+ * @return class code
+ */
+ ClassCode classCode();
+
+ /**
+ * Type of registry of this descriptor.
+ *
+ * @return registry type
+ */
+ String registryType();
+
+ /**
+ * Weight of the new descriptor.
+ *
+ * @return weight
+ */
+ double weight();
+
+ /**
+ * Contracts the described service implements/provides.
+ *
+ * @return contracts of the service
+ */
+ Set contracts();
+}
diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Little.java b/service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCodeImpl.java
similarity index 57%
rename from examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Little.java
rename to service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCodeImpl.java
index 1eb289d4644..fc78e2781a4 100644
--- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Little.java
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/DescriptorClassCodeImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2024 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.
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package io.helidon.examples.inject.basics;
+package io.helidon.service.codegen;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
-import jakarta.inject.Qualifier;
+import io.helidon.codegen.ClassCode;
+import io.helidon.common.types.TypeName;
-/**
- * Custom annotation.
- */
-@Qualifier
-@Retention(RetentionPolicy.CLASS)
-public @interface Little {
+record DescriptorClassCodeImpl(ClassCode classCode,
+ String registryType,
+ double weight,
+ Set contracts) implements DescriptorClassCode {
}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/GenerateServiceDescriptor.java b/service/codegen/src/main/java/io/helidon/service/codegen/GenerateServiceDescriptor.java
new file mode 100644
index 00000000000..96715a37c14
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/GenerateServiceDescriptor.java
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2024 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.service.codegen;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+import io.helidon.codegen.CodegenException;
+import io.helidon.codegen.CodegenUtil;
+import io.helidon.codegen.ElementInfoPredicates;
+import io.helidon.codegen.classmodel.ClassModel;
+import io.helidon.codegen.classmodel.Javadoc;
+import io.helidon.codegen.classmodel.Method;
+import io.helidon.codegen.classmodel.TypeArgument;
+import io.helidon.common.Weight;
+import io.helidon.common.Weighted;
+import io.helidon.common.types.AccessModifier;
+import io.helidon.common.types.Annotation;
+import io.helidon.common.types.Annotations;
+import io.helidon.common.types.ElementKind;
+import io.helidon.common.types.Modifier;
+import io.helidon.common.types.TypeInfo;
+import io.helidon.common.types.TypeName;
+import io.helidon.common.types.TypeNames;
+import io.helidon.common.types.TypedElementInfo;
+
+import static io.helidon.service.codegen.ServiceCodegenTypes.SERVICE_ANNOTATION_PROVIDER;
+import static java.util.function.Predicate.not;
+
+/**
+ * Generates a service descriptor.
+ */
+class GenerateServiceDescriptor {
+ static final TypeName SET_OF_TYPES = TypeName.builder(TypeNames.SET)
+ .addTypeArgument(TypeNames.TYPE_NAME)
+ .build();
+ private static final TypeName LIST_OF_DEPENDENCIES = TypeName.builder(TypeNames.LIST)
+ .addTypeArgument(ServiceCodegenTypes.SERVICE_DEPENDENCY)
+ .build();
+ private static final TypeName DESCRIPTOR_TYPE = TypeName.builder(ServiceCodegenTypes.SERVICE_DESCRIPTOR)
+ .addTypeArgument(TypeName.create("T"))
+ .build();
+ private static final TypedElementInfo DEFAULT_CONSTRUCTOR = TypedElementInfo.builder()
+ .typeName(TypeNames.OBJECT)
+ .accessModifier(AccessModifier.PUBLIC)
+ .kind(ElementKind.CONSTRUCTOR)
+ .build();
+ private static final TypeName ANY_GENERIC_TYPE = TypeName.builder(TypeNames.GENERIC_TYPE)
+ .addTypeArgument(TypeName.create("?"))
+ .build();
+
+ private final TypeName generator;
+ private final RegistryCodegenContext ctx;
+ private final Collection services;
+ private final TypeInfo typeInfo;
+ private final boolean autoAddContracts;
+
+ private GenerateServiceDescriptor(TypeName generator,
+ RegistryCodegenContext ctx,
+ Collection allServices,
+ TypeInfo service) {
+ this.generator = generator;
+ this.ctx = ctx;
+ this.services = allServices;
+ this.typeInfo = service;
+ this.autoAddContracts = ServiceOptions.AUTO_ADD_NON_CONTRACT_INTERFACES.value(ctx.options());
+ }
+
+ /**
+ * Generate a service descriptor for the provided service type info.
+ *
+ * @param generator type of the generator responsible for this event
+ * @param ctx context of code generation
+ * @param allServices all services processed in this round of processing
+ * @param service service to create a descriptor for
+ * @return class model builder of the service descriptor
+ */
+ static ClassModel.Builder generate(TypeName generator,
+ RegistryCodegenContext ctx,
+ Collection allServices,
+ TypeInfo service) {
+ return new GenerateServiceDescriptor(generator, ctx, allServices, service)
+ .generate();
+ }
+
+ static List declareCtrParamsAndGetThem(Method.Builder method, List params) {
+ List constructorParams = params.stream()
+ .filter(it -> it.kind() == ElementKind.CONSTRUCTOR)
+ .toList();
+
+ // for each parameter, obtain its value from context
+ for (ParamDefinition param : constructorParams) {
+ method.addContent(param.declaredType())
+ .addContent(" ")
+ .addContent(param.ipParamName())
+ .addContent(" = ")
+ .update(it -> param.assignmentHandler().accept(it))
+ .addContentLine(";");
+ }
+ if (!params.isEmpty()) {
+ method.addContentLine("");
+ }
+ return constructorParams;
+ }
+
+ private ClassModel.Builder generate() {
+ TypeName serviceType = typeInfo.typeName();
+
+ if (typeInfo.kind() == ElementKind.INTERFACE) {
+ throw new CodegenException("We can only generated service descriptors for classes, interface was requested: ",
+ typeInfo.originatingElement().orElse(serviceType));
+ }
+ boolean isAbstractClass = typeInfo.elementModifiers().contains(Modifier.ABSTRACT)
+ && typeInfo.kind() == ElementKind.CLASS;
+
+ SuperType superType = superType(typeInfo, services);
+
+ // this must result in generating a service descriptor file
+ TypeName descriptorType = ctx.descriptorType(serviceType);
+
+ List params = params(typeInfo, constructor(typeInfo));
+
+ ClassModel.Builder classModel = ClassModel.builder()
+ .copyright(CodegenUtil.copyright(generator,
+ serviceType,
+ descriptorType))
+ .addAnnotation(CodegenUtil.generatedAnnotation(generator,
+ serviceType,
+ descriptorType,
+ "1",
+ ""))
+ .type(descriptorType)
+ .addGenericArgument(TypeArgument.create("T extends " + serviceType.fqName()))
+ .javadoc(Javadoc.builder()
+ .add("Service descriptor for {@link " + serviceType.fqName() + "}.")
+ .addGenericArgument("T", "type of the service, for extensibility")
+ .build())
+ // we need to keep insertion order, as constants may depend on each other
+ .sortStaticFields(false);
+
+ Map genericTypes = genericTypes(classModel, params);
+ Set contracts = new HashSet<>();
+ Set collectedFullyQualifiedContracts = new HashSet<>();
+ contracts(typeInfo, autoAddContracts, contracts, collectedFullyQualifiedContracts);
+
+ // declare the class
+
+ if (superType.hasSupertype()) {
+ classModel.superType(superType.superDescriptorType());
+ } else {
+ classModel.addInterface(DESCRIPTOR_TYPE);
+ }
+
+ // Fields
+ singletonInstanceField(classModel, serviceType, descriptorType);
+ serviceTypeFields(classModel, serviceType, descriptorType);
+
+ // public fields are last, so they do not intersect with private fields (it is not as nice to read)
+ // they cannot be first, as they require some of the private fields
+ dependencyFields(classModel, typeInfo, genericTypes, params);
+ // dependencies require IP IDs, so they really must be last
+ dependenciesField(classModel, params);
+
+ // add protected constructor
+ classModel.addConstructor(constructor -> constructor.description("Constructor with no side effects")
+ .accessModifier(AccessModifier.PROTECTED));
+
+ // methods (some methods define fields as well)
+ serviceTypeMethod(classModel);
+ descriptorTypeMethod(classModel);
+ contractsMethod(classModel, contracts);
+ dependenciesMethod(classModel, params, superType);
+ isAbstractMethod(classModel, superType, isAbstractClass);
+ instantiateMethod(classModel, serviceType, params, isAbstractClass);
+ weightMethod(typeInfo, classModel, superType);
+
+ // service type is an implicit contract
+ Set allContracts = new HashSet<>(contracts);
+ allContracts.add(serviceType);
+
+ ctx.addDescriptor("core",
+ serviceType,
+ descriptorType,
+ classModel,
+ weight(typeInfo).orElse(Weighted.DEFAULT_WEIGHT),
+ allContracts,
+ typeInfo.originatingElement().orElseGet(typeInfo::typeName));
+
+ return classModel;
+ }
+
+ private SuperType superType(TypeInfo typeInfo, Collection services) {
+ // find super type if it is also a service (or has a service descriptor)
+
+ // check if the super type is part of current annotation processing
+ Optional superTypeInfoOptional = typeInfo.superTypeInfo();
+ if (superTypeInfoOptional.isEmpty()) {
+ return SuperType.noSuperType();
+ }
+ TypeInfo superType = superTypeInfoOptional.get();
+ TypeName expectedSuperDescriptor = ctx.descriptorType(superType.typeName());
+ TypeName superTypeToExtend = TypeName.builder(expectedSuperDescriptor)
+ .addTypeArgument(TypeName.create("T"))
+ .build();
+ boolean isCore = superType.hasAnnotation(SERVICE_ANNOTATION_PROVIDER);
+ if (!isCore) {
+ throw new CodegenException("Service annotated with @Service.Provider extends invalid supertype,"
+ + " the super type must also be a @Service.Provider. Type: "
+ + typeInfo.typeName().fqName() + ", super type: "
+ + superType.typeName().fqName());
+ }
+ for (TypeInfo service : services) {
+ if (service.typeName().equals(superType.typeName())) {
+ return new SuperType(true, superTypeToExtend, service, true);
+ }
+ }
+ // if not found in current list, try checking existing types
+ return ctx.typeInfo(expectedSuperDescriptor)
+ .map(it -> new SuperType(true, superTypeToExtend, superType, true))
+ .orElseGet(SuperType::noSuperType);
+ }
+
+ // there must be none, or one non-private constructor (actually there may be more, we just use the first)
+ private TypedElementInfo constructor(TypeInfo typeInfo) {
+ return typeInfo.elementInfo()
+ .stream()
+ .filter(it -> it.kind() == ElementKind.CONSTRUCTOR)
+ .filter(not(ElementInfoPredicates::isPrivate))
+ .findFirst()
+ // or default constructor
+ .orElse(DEFAULT_CONSTRUCTOR);
+ }
+
+ private List params(
+ TypeInfo service,
+ TypedElementInfo constructor) {
+ AtomicInteger paramCounter = new AtomicInteger();
+
+ return constructor.parameterArguments()
+ .stream()
+ .map(param -> {
+ String constantName = "PARAM_" + paramCounter.getAndIncrement();
+ RegistryCodegenContext.Assignment assignment = translateParameter(param.typeName(), constantName);
+ return new ParamDefinition(constructor,
+ null,
+ param,
+ constantName,
+ param.typeName(),
+ assignment.usedType(),
+ assignment.codeGenerator(),
+ ElementKind.CONSTRUCTOR,
+ constructor.elementName(),
+ param.elementName(),
+ param.elementName(),
+ false,
+ param.annotations(),
+ Set.of(),
+ contract(service.typeName()
+ .fqName() + " Constructor parameter: " + param.elementName(),
+ assignment.usedType()),
+ constructor.accessModifier(),
+ "");
+ })
+ .toList();
+ }
+
+ private TypeName contract(String description, TypeName typeName) {
+ /*
+ get the contract expected for this dependency
+ IP may be:
+ - Optional
+ - List
+ - ServiceProvider
+ - Supplier
+ - Optional
+ - Optional
+ - List
+ - List
+ */
+
+ if (typeName.isOptional()) {
+ if (typeName.typeArguments().isEmpty()) {
+ throw new IllegalArgumentException("Dependency with Optional type must have a declared type argument: "
+ + description);
+ }
+ return contract(description, typeName.typeArguments().getFirst());
+ }
+ if (typeName.isList()) {
+ if (typeName.typeArguments().isEmpty()) {
+ throw new IllegalArgumentException("Dependency with List type must have a declared type argument: "
+ + description);
+ }
+ return contract(description, typeName.typeArguments().getFirst());
+ }
+ if (typeName.isSupplier()) {
+ if (typeName.typeArguments().isEmpty()) {
+ throw new IllegalArgumentException("Dependency with Supplier type must have a declared type argument: "
+ + description);
+ }
+ return contract(description, typeName.typeArguments().getFirst());
+ }
+
+ return typeName;
+ }
+
+ private Map genericTypes(ClassModel.Builder classModel,
+ List params) {
+ // we must use map by string (as type name is equal if the same class, not full generic declaration)
+ Map result = new LinkedHashMap<>();
+ AtomicInteger counter = new AtomicInteger();
+
+ for (ParamDefinition param : params) {
+ result.computeIfAbsent(param.translatedType().resolvedName(),
+ type -> {
+ var response =
+ new GenericTypeDeclaration("TYPE_" + counter.getAndIncrement(),
+ param.declaredType());
+ addTypeConstant(classModel, param.translatedType(), response);
+ return response;
+ });
+ result.computeIfAbsent(param.contract().fqName(),
+ type -> {
+ var response =
+ new GenericTypeDeclaration("TYPE_" + counter.getAndIncrement(),
+ param.declaredType());
+ addTypeConstant(classModel, param.contract(), response);
+ return response;
+ });
+ }
+
+ return result;
+ }
+
+ private void addTypeConstant(ClassModel.Builder classModel,
+ TypeName typeName,
+ GenericTypeDeclaration generic) {
+ String stringType = typeName.resolvedName();
+ // constants for dependency parameter types (used by next section)
+ classModel.addField(field -> field
+ .accessModifier(AccessModifier.PRIVATE)
+ .isStatic(true)
+ .isFinal(true)
+ .type(TypeNames.TYPE_NAME)
+ .name(generic.constantName())
+ .update(it -> {
+ if (stringType.indexOf('.') < 0) {
+ // there is no package, we must use class (if this is a generic type, we have a problem)
+ it.addContent(TypeNames.TYPE_NAME)
+ .addContent(".create(")
+ .addContent(typeName)
+ .addContent(".class)");
+ } else {
+ it.addContentCreate(typeName);
+ }
+ }));
+ classModel.addField(field -> field
+ .accessModifier(AccessModifier.PRIVATE)
+ .isStatic(true)
+ .isFinal(true)
+ .type(ANY_GENERIC_TYPE)
+ .name("G" + generic.constantName())
+ .update(it -> {
+ if (typeName.primitive()) {
+ it.addContent(TypeNames.GENERIC_TYPE)
+ .addContent(".create(")
+ .addContent(typeName.className())
+ .addContent(".class)");
+ } else {
+ it.addContent("new ")
+ .addContent(TypeNames.GENERIC_TYPE)
+ .addContent("<")
+ .addContent(typeName)
+ .addContent(">() {}");
+ }
+ })
+ );
+ }
+
+ private void contracts(TypeInfo typeInfo,
+ boolean contractEligible,
+ Set collectedContracts,
+ Set collectedFullyQualified) {
+ TypeName typeName = typeInfo.typeName();
+
+ boolean addedThisContract = false;
+ if (contractEligible) {
+ collectedContracts.add(typeName);
+ addedThisContract = true;
+ if (!collectedFullyQualified.add(typeName.resolvedName())) {
+ // let us go no further, this type was already processed
+ return;
+ }
+ }
+
+ if (typeName.isSupplier()) {
+ // this may be the interface itself, and then it does not have a type argument
+ if (!typeName.typeArguments().isEmpty()) {
+ // provider must have a type argument (and the type argument is an automatic contract
+ TypeName providedType = typeName.typeArguments().getFirst();
+ if (!providedType.generic()) {
+ Optional providedTypeInfo = ctx.typeInfo(providedType);
+ if (providedTypeInfo.isPresent()) {
+ contracts(providedTypeInfo.get(),
+ true,
+ collectedContracts,
+ collectedFullyQualified
+ );
+ } else {
+ collectedContracts.add(providedType);
+ if (!collectedFullyQualified.add(providedType.resolvedName())) {
+ // let us go no further, this type was already processed
+ return;
+ }
+ }
+ }
+ }
+
+ // provider itself is a contract
+ if (!addedThisContract) {
+ collectedContracts.add(typeName);
+ if (!collectedFullyQualified.add(typeName.resolvedName())) {
+ // let us go no further, this type was already processed
+ return;
+ }
+ }
+ }
+
+ // add contracts from interfaces and types annotated as @Contract
+ typeInfo.findAnnotation(ServiceCodegenTypes.SERVICE_ANNOTATION_CONTRACT)
+ .ifPresent(it -> collectedContracts.add(typeInfo.typeName()));
+
+ // add contracts from @ExternalContracts
+ typeInfo.findAnnotation(ServiceCodegenTypes.SERVICE_ANNOTATION_EXTERNAL_CONTRACTS)
+ .ifPresent(it -> collectedContracts.addAll(it.typeValues().orElseGet(List::of)));
+
+ // go through hierarchy
+ typeInfo.superTypeInfo().ifPresent(it -> contracts(it,
+ contractEligible,
+ collectedContracts,
+ collectedFullyQualified
+ ));
+ // interfaces are considered contracts by default
+ typeInfo.interfaceTypeInfo().forEach(it -> contracts(it,
+ contractEligible,
+ collectedContracts,
+ collectedFullyQualified
+ ));
+ }
+
+ private void singletonInstanceField(ClassModel.Builder classModel, TypeName serviceType, TypeName descriptorType) {
+ // singleton instance of the descriptor
+ classModel.addField(instance -> instance.description("Global singleton instance for this descriptor.")
+ .accessModifier(AccessModifier.PUBLIC)
+ .isStatic(true)
+ .isFinal(true)
+ .type(descriptorInstanceType(serviceType, descriptorType))
+ .name("INSTANCE")
+ .defaultValueContent("new " + descriptorType.className() + "<>()"));
+ }
+
+ private void serviceTypeFields(ClassModel.Builder classModel, TypeName serviceType, TypeName descriptorType) {
+ classModel.addField(field -> field
+ .isStatic(true)
+ .isFinal(true)
+ .accessModifier(AccessModifier.PRIVATE)
+ .type(TypeNames.TYPE_NAME)
+ .name("SERVICE_TYPE")
+ .addContentCreate(serviceType.genericTypeName()));
+
+ classModel.addField(field -> field
+ .isStatic(true)
+ .isFinal(true)
+ .accessModifier(AccessModifier.PRIVATE)
+ .type(TypeNames.TYPE_NAME)
+ .name("DESCRIPTOR_TYPE")
+ .addContentCreate(descriptorType.genericTypeName()));
+ }
+
+ private void dependencyFields(ClassModel.Builder classModel,
+ TypeInfo service,
+ Map genericTypes,
+ List params) {
+ // constant for dependency
+ for (ParamDefinition param : params) {
+ classModel.addField(field -> field
+ .accessModifier(AccessModifier.PUBLIC)
+ .isStatic(true)
+ .isFinal(true)
+ .type(ServiceCodegenTypes.SERVICE_DEPENDENCY)
+ .name(param.constantName())
+ .description(dependencyDescription(service, param))
+ .update(it -> {
+ it.addContent(ServiceCodegenTypes.SERVICE_DEPENDENCY)
+ .addContentLine(".builder()")
+ .increaseContentPadding()
+ .increaseContentPadding()
+ .addContent(".typeName(")
+ .addContent(genericTypes.get(param.translatedType().resolvedName()).constantName())
+ .addContentLine(")")
+ .update(maybeElementKind -> {
+ if (param.kind() != ElementKind.CONSTRUCTOR) {
+ // constructor is default and does not need to be defined
+ maybeElementKind.addContent(".elementKind(")
+ .addContent(TypeNames.ELEMENT_KIND)
+ .addContent(".")
+ .addContent(param.kind().name())
+ .addContentLine(")");
+ }
+ })
+ .update(maybeMethod -> {
+ if (param.kind() == ElementKind.METHOD) {
+ maybeMethod.addContent(".method(")
+ .addContent(param.methodConstantName())
+ .addContentLine(")");
+ }
+ })
+ .addContent(".name(\"")
+ .addContent(param.fieldId())
+ .addContentLine("\")")
+ .addContentLine(".service(SERVICE_TYPE)")
+ .addContentLine(".descriptor(DESCRIPTOR_TYPE)")
+ .addContent(".descriptorConstant(\"")
+ .addContent(param.constantName())
+ .addContentLine("\")")
+ .addContent(".contract(")
+ .addContent(genericTypes.get(param.contract().fqName()).constantName())
+ .addContentLine(")")
+ .addContent(".contractType(G")
+ .addContent(genericTypes.get(param.contract().fqName()).constantName())
+ .addContentLine(")");
+ if (param.access() != AccessModifier.PACKAGE_PRIVATE) {
+ it.addContent(".access(")
+ .addContent(TypeNames.ACCESS_MODIFIER)
+ .addContent(".")
+ .addContent(param.access().name())
+ .addContentLine(")");
+ }
+
+ if (param.isStatic()) {
+ it.addContentLine(".isStatic(true)");
+ }
+
+ if (!param.qualifiers().isEmpty()) {
+ for (Annotation qualifier : param.qualifiers()) {
+ it.addContent(".addQualifier(qualifier -> qualifier.typeName(")
+ .addContentCreate(qualifier.typeName().genericTypeName())
+ .addContent(")");
+ qualifier.value().ifPresent(q -> it.addContent(".value(\"")
+ .addContent(q)
+ .addContent("\")"));
+ it.addContentLine(")");
+ }
+ }
+
+ it.addContent(".build()")
+ .decreaseContentPadding()
+ .decreaseContentPadding();
+ }));
+ }
+ }
+
+ private String dependencyDescription(TypeInfo service, ParamDefinition param) {
+ TypeName serviceType = service.typeName();
+ StringBuilder result = new StringBuilder("Dependency for ");
+ boolean servicePublic = service.accessModifier() == AccessModifier.PUBLIC;
+ boolean elementPublic = param.owningElement().accessModifier() == AccessModifier.PUBLIC;
+
+ if (servicePublic) {
+ result.append("{@link ")
+ .append(serviceType.fqName());
+ if (!elementPublic) {
+ result.append("}");
+ }
+ } else {
+ result.append(serviceType.classNameWithEnclosingNames());
+ }
+
+ if (servicePublic && elementPublic) {
+ // full javadoc reference
+ result
+ .append("#")
+ .append(serviceType.className())
+ .append("(")
+ .append(toDescriptionSignature(param.owningElement(), true))
+ .append(")")
+ .append("}");
+ } else {
+ // just text
+ result.append("(")
+ .append(toDescriptionSignature(param.owningElement(), false))
+ .append(")");
+ }
+
+ result
+ .append(", parameter ")
+ .append(param.elementInfo().elementName())
+ .append(".");
+ return result.toString();
+ }
+
+ private String toDescriptionSignature(TypedElementInfo method, boolean javadoc) {
+ if (javadoc) {
+ return method.parameterArguments()
+ .stream()
+ .map(it -> it.typeName().fqName())
+ .collect(Collectors.joining(", "));
+ } else {
+ return method.parameterArguments()
+ .stream()
+ .map(it -> it.typeName().classNameWithEnclosingNames() + " " + it.elementName())
+ .collect(Collectors.joining(", "));
+ }
+ }
+
+ private void dependenciesField(ClassModel.Builder classModel, List params) {
+ classModel.addField(dependencies -> dependencies
+ .isStatic(true)
+ .isFinal(true)
+ .name("DEPENDENCIES")
+ .type(LIST_OF_DEPENDENCIES)
+ .addContent(List.class)
+ .addContent(".of(")
+ .update(it -> {
+ Iterator iterator = params.iterator();
+ while (iterator.hasNext()) {
+ it.addContent(iterator.next().constantName());
+ if (iterator.hasNext()) {
+ it.addContent(", ");
+ }
+ }
+ })
+ .addContent(")"));
+ }
+
+ private void serviceTypeMethod(ClassModel.Builder classModel) {
+ // TypeName serviceType()
+ classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
+ .returnType(TypeNames.TYPE_NAME)
+ .name("serviceType")
+ .addContentLine("return SERVICE_TYPE;"));
+ }
+
+ private void descriptorTypeMethod(ClassModel.Builder classModel) {
+ // TypeName descriptorType()
+ classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
+ .returnType(TypeNames.TYPE_NAME)
+ .name("descriptorType")
+ .addContentLine("return DESCRIPTOR_TYPE;"));
+ }
+
+ private void contractsMethod(ClassModel.Builder classModel, Set contracts) {
+ if (contracts.isEmpty()) {
+ return;
+ }
+ classModel.addField(contractsField -> contractsField
+ .isStatic(true)
+ .isFinal(true)
+ .name("CONTRACTS")
+ .type(SET_OF_TYPES)
+ .addContent(Set.class)
+ .addContent(".of(")
+ .update(it -> {
+ Iterator iterator = contracts.iterator();
+ while (iterator.hasNext()) {
+ it.addContentCreate(iterator.next().genericTypeName());
+ if (iterator.hasNext()) {
+ it.addContent(", ");
+ }
+ }
+ })
+ .addContent(")"));
+
+ // Set> contracts()
+ classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
+ .name("contracts")
+ .returnType(SET_OF_TYPES)
+ .addContentLine("return CONTRACTS;"));
+ }
+
+ private void dependenciesMethod(ClassModel.Builder classModel, List params, SuperType superType) {
+ // List dependencies()
+ boolean hasSuperType = superType.hasSupertype();
+ if (hasSuperType || !params.isEmpty()) {
+ classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
+ .returnType(LIST_OF_DEPENDENCIES)
+ .name("dependencies")
+ .update(it -> {
+ if (hasSuperType) {
+ it.addContentLine("return combineDependencies(DEPENDENCIES, super.dependencies());");
+ } else {
+ it.addContentLine("return DEPENDENCIES;");
+ }
+ }));
+ }
+ }
+
+ private void instantiateMethod(ClassModel.Builder classModel,
+ TypeName serviceType,
+ List params,
+ boolean isAbstractClass) {
+ if (isAbstractClass) {
+ return;
+ }
+
+ // T instantiate(DependencyContext ctx__helidonRegistry)
+ classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
+ .returnType(serviceType)
+ .name("instantiate")
+ .addParameter(ctxParam -> ctxParam.type(ServiceCodegenTypes.SERVICE_DEPENDENCY_CONTEXT)
+ .name("ctx__helidonRegistry"))
+ .update(it -> createInstantiateBody(serviceType, it, params)));
+ }
+
+ private void createInstantiateBody(TypeName serviceType,
+ Method.Builder method,
+ List params) {
+ List constructorParams = declareCtrParamsAndGetThem(method, params);
+ String paramsDeclaration = constructorParams.stream()
+ .map(ParamDefinition::ipParamName)
+ .collect(Collectors.joining(", "));
+
+ // return new MyImpl(parameter, parameter2)
+ method.addContent("return new ")
+ .addContent(serviceType.genericTypeName())
+ .addContent("(")
+ .addContent(paramsDeclaration)
+ .addContentLine(");");
+ }
+
+ private void isAbstractMethod(ClassModel.Builder classModel, SuperType superType, boolean isAbstractClass) {
+ if (!isAbstractClass && !superType.hasSupertype()) {
+ return;
+ }
+ // only override for abstract types (and subtypes, where we do not want to check if super is abstract), default is false
+ classModel.addMethod(isAbstract -> isAbstract
+ .name("isAbstract")
+ .returnType(TypeNames.PRIMITIVE_BOOLEAN)
+ .addAnnotation(Annotations.OVERRIDE)
+ .addContentLine("return " + isAbstractClass + ";"));
+ }
+
+ private void weightMethod(TypeInfo typeInfo, ClassModel.Builder classModel, SuperType superType) {
+ boolean hasSuperType = superType.hasSupertype();
+ // double weight()
+ Optional weight = weight(typeInfo);
+
+ if (!hasSuperType && weight.isEmpty()) {
+ return;
+ }
+ double usedWeight = weight.orElse(Weighted.DEFAULT_WEIGHT);
+ if (!hasSuperType && usedWeight == Weighted.DEFAULT_WEIGHT) {
+ return;
+ }
+
+ classModel.addMethod(weightMethod -> weightMethod.name("weight")
+ .addAnnotation(Annotations.OVERRIDE)
+ .returnType(TypeNames.PRIMITIVE_DOUBLE)
+ .addContentLine("return " + usedWeight + ";"));
+ }
+
+ private Optional weight(TypeInfo typeInfo) {
+ return typeInfo.findAnnotation(TypeName.create(Weight.class))
+ .flatMap(Annotation::doubleValue);
+ }
+
+ private RegistryCodegenContext.Assignment translateParameter(TypeName typeName, String constantName) {
+ return ctx.assignment(typeName, "ctx__helidonRegistry.dependency(" + constantName + ")");
+ }
+
+ private TypeName descriptorInstanceType(TypeName serviceType, TypeName descriptorType) {
+ return TypeName.builder(descriptorType)
+ .addTypeArgument(serviceType)
+ .build();
+ }
+
+ private record GenericTypeDeclaration(String constantName,
+ TypeName typeName) {
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/HelidonMetaInfServices.java b/service/codegen/src/main/java/io/helidon/service/codegen/HelidonMetaInfServices.java
new file mode 100644
index 00000000000..363d41c2aa7
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/HelidonMetaInfServices.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2024 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.service.codegen;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import io.helidon.codegen.CodegenException;
+import io.helidon.codegen.CodegenFiler;
+import io.helidon.codegen.FilerTextResource;
+import io.helidon.common.types.Annotation;
+import io.helidon.common.types.TypeName;
+
+import static io.helidon.codegen.CodegenUtil.generatedAnnotation;
+
+/**
+ * Support for reading and writing Helidon services to the resource.
+ *
+ * Helidon replacement for Java {@link java.util.ServiceLoader}.
+ * Each service annotated with appropriate annotation
+ * ({@link io.helidon.service.codegen.ServiceCodegenTypes#SERVICE_ANNOTATION_PROVIDER})
+ * will have a service descriptor generated at build time.
+ *
+ * The service descriptor is then discoverable at runtime through our own resource in {@value #SERVICES_RESOURCE}.
+ */
+class HelidonMetaInfServices {
+ /**
+ * Location of the Helidon meta services file.
+ */
+ static final String SERVICES_RESOURCE = "META-INF/helidon/service.registry";
+
+ private final FilerTextResource services;
+ private final List comments;
+ private final Set descriptors;
+
+ private HelidonMetaInfServices(FilerTextResource services, List comments, Set descriptors) {
+ this.services = services;
+ this.comments = comments;
+ this.descriptors = descriptors;
+ }
+
+ /**
+ * Create new instance from the current filer.
+ *
+ * @param filer filer to find the file, and to write it
+ * @param generator generator used in generated comment if we are creating a new file
+ * @param trigger trigger that caused this file to be created (can be same as generator)
+ * @param moduleName module that is being built
+ * @return a new instance of the service metadata manager
+ */
+ static HelidonMetaInfServices create(CodegenFiler filer, TypeName generator, TypeName trigger, String moduleName) {
+ FilerTextResource services = filer.textResource(SERVICES_RESOURCE);
+
+ List comments = new ArrayList<>();
+ Set descriptors = new TreeSet<>(Comparator.comparing(DescriptorMeta::descriptor));
+
+ for (String line : services.lines()) {
+ String trimmedLine = line.trim();
+ if (trimmedLine.startsWith("#")) {
+ comments.add(line);
+ } else if (trimmedLine.isEmpty()) {
+ // ignore empty lines
+ continue;
+ } else {
+ descriptors.add(DescriptorMeta.parse(trimmedLine));
+ }
+ }
+
+ if (comments.isEmpty()) {
+ // @Generated
+ comments.add("# Generated list of service descriptors in module " + moduleName);
+ comments.add("# " + toAnnotationText(generatedAnnotation(generator,
+ trigger,
+ TypeName.create("io.helidon.services.ServicesMeta"),
+ "1",
+ "")));
+ }
+ return new HelidonMetaInfServices(services, comments, descriptors);
+ }
+
+ /**
+ * Add all descriptors to the file. This never produces duplicate records.
+ * Descriptor type name is always unique within a file.
+ *
+ * @param services service descriptor metadata to add
+ */
+ void addAll(Collection services) {
+ services.forEach(this::add);
+ }
+
+ /**
+ * Add a single descriptors to the file. This never produces duplicate records.
+ * Descriptor type name is always unique within a file.
+ *
+ * @param service service descriptor metadata to add
+ */
+ void add(DescriptorMeta service) {
+ // if it is the same descriptor class, remove it
+ descriptors.removeIf(it -> it.descriptor().equals(service.descriptor()));
+
+ // always add the new descriptor (either it does not exist, or it was deleted)
+ descriptors.add(service);
+ }
+
+ /**
+ * Write the file to output.
+ */
+ void write() {
+ List lines = new ArrayList<>(comments);
+ descriptors.stream()
+ .map(DescriptorMeta::toLine)
+ .forEach(lines::add);
+ services.lines(lines);
+ services.write();
+ }
+
+ private static String toAnnotationText(Annotation annotation) {
+ List valuePairs = new ArrayList<>();
+ Map annotationValues = annotation.values();
+ annotationValues.forEach((key, value) -> valuePairs.add(key + "=\"" + value + "\""));
+ return "@" + annotation.typeName().fqName() + "(" + String.join(", ", valuePairs) + ")";
+ }
+
+ /**
+ * Metadata of a single service descriptor.
+ * This information is stored within the Helidon specific {code META-INF} services file.
+ *
+ * @param registryType type of registry, such as {@code core}
+ * @param descriptor descriptor type
+ * @param weight weight of the service
+ * @param contracts contracts the service implements/provides
+ */
+ record DescriptorMeta(String registryType, TypeName descriptor, double weight, Set contracts) {
+ private static DescriptorMeta parse(String line) {
+ String[] elements = line.split(":");
+ if (elements.length < 4) {
+ throw new CodegenException("Failed to parse line from existing META-INF/helidon/service.registry: "
+ + line + ", expecting registry-type:serviceDescriptor:weight:contracts");
+ }
+ Set contracts = Stream.of(elements[3].split(","))
+ .map(String::trim)
+ .map(TypeName::create)
+ .collect(Collectors.toSet());
+
+ try {
+ return new DescriptorMeta(elements[0],
+ TypeName.create(elements[1]),
+ Double.parseDouble(elements[2]),
+ contracts);
+ } catch (NumberFormatException e) {
+ throw new CodegenException("Unexpected line structure in service.registry. Third element should be weight "
+ + "(double). Got: " + line + ", message: " + e.getMessage(), e);
+ }
+ }
+
+ private String toLine() {
+ return registryType + ":"
+ + descriptor.fqName() + ":"
+ + weight + ":"
+ + contracts.stream()
+ .map(TypeName::fqName)
+ .collect(Collectors.joining(","));
+ }
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/ParamDefinition.java b/service/codegen/src/main/java/io/helidon/service/codegen/ParamDefinition.java
new file mode 100644
index 00000000000..28b0f0fd2c4
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/ParamDefinition.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2024 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.service.codegen;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import io.helidon.codegen.classmodel.ContentBuilder;
+import io.helidon.common.types.AccessModifier;
+import io.helidon.common.types.Annotation;
+import io.helidon.common.types.ElementKind;
+import io.helidon.common.types.TypeName;
+import io.helidon.common.types.TypedElementInfo;
+
+/**
+ * Definition of a single parameter (probably a dependency).
+ *
+ * @param owningElement if this is an argument, the constructor or method this belongs to
+ * @param methodConstantName in case this param belongs to a method, the constant of the method, otherwise null
+ * @param elementInfo element info of field or argument
+ * @param constantName name of the constant that holds the IpId of this parameter
+ * @param declaredType type of the field as required by the dependency
+ * @param translatedType type used for fulfilling this dependency (e.g. using Supplier where #type uses Provider),
+ * same instance as #type if not translated
+ * @param assignmentHandler to provide source for assigning the result from dependency context
+ * @param kind kind of the owning element (field, method, constructor)
+ * @param ipName name of the field or method
+ * @param ipParamName name of the field or parameter
+ * @param fieldId unique identification of this param within the type (field name, methodid + param name)
+ * @param isStatic whether the field is static
+ * @param annotations annotations on this dependency
+ * @param qualifiers qualifiers of this dependency
+ * @param contract contract expected for this dependency (ignoring list, supplier, optional etc.)
+ * @param access access modifier of this param
+ * @param methodId id of the method (unique identification of method within the class)
+ */
+record ParamDefinition(TypedElementInfo owningElement,
+ String methodConstantName,
+ TypedElementInfo elementInfo,
+ String constantName,
+ TypeName declaredType,
+ TypeName translatedType,
+ Consumer> assignmentHandler,
+ ElementKind kind,
+ String ipName,
+ String ipParamName,
+ String fieldId,
+ boolean isStatic,
+ List annotations,
+ Set qualifiers,
+ TypeName contract,
+ AccessModifier access,
+ String methodId) {
+
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContext.java b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContext.java
new file mode 100644
index 00000000000..6066e8f90a6
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContext.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import io.helidon.codegen.ClassCode;
+import io.helidon.codegen.CodegenContext;
+import io.helidon.codegen.classmodel.ClassModel;
+import io.helidon.codegen.classmodel.ContentBuilder;
+import io.helidon.common.types.TypeName;
+
+/**
+ * Codegen context adding methods suitable for Helidon Service Registry code generation.
+ */
+interface RegistryCodegenContext extends CodegenContext {
+ /**
+ * Create a new instance from an existing code generation context.
+ *
+ * @param context code generation context of the current code generation session
+ * @return a new Helidon Service Registry code generation context
+ */
+ static RegistryCodegenContext create(CodegenContext context) {
+ return new RegistryCodegenContextImpl(context);
+ }
+
+ /**
+ * Service descriptor of a type that is already created. This allows extensions with lower weight to update
+ * the code generated descriptor after it was generated.
+ *
+ * @param serviceType type of the service (the implementation class we generate descriptor for)
+ * @return the builder of class model, if the service has a descriptor
+ */
+ Optional descriptor(TypeName serviceType);
+
+ /**
+ * Add a new service descriptor.
+ *
+ * @param registryType service registry this descriptor is designed for (core is the "top" level)
+ * @param serviceType type of the service (the implementation class we generate descriptor for)
+ * @param descriptorType type of the service descriptor
+ * @param descriptor descriptor class model
+ * @param weight weight of this service descriptor
+ * @param contracts contracts of this service descriptor
+ * @param originatingElements possible originating elements (such as Element in APT, or ClassInfo in classpath scanning)
+ * @throws java.lang.IllegalStateException if an attempt is done to register a new descriptor for the same type
+ */
+ void addDescriptor(String registryType,
+ TypeName serviceType,
+ TypeName descriptorType,
+ ClassModel.Builder descriptor,
+ double weight,
+ Set contracts,
+ Object... originatingElements);
+
+ /**
+ * Add a new class to be code generated.
+ *
+ * @param type type of the new class
+ * @param newClass builder of the new class
+ * @param mainTrigger a type that caused this, may be the processor itself, if not bound to any type
+ * @param originatingElements possible originating elements (such as Element in APT, or ClassInfo in classpath scanning)
+ */
+ void addType(TypeName type, ClassModel.Builder newClass, TypeName mainTrigger, Object... originatingElements);
+
+ /**
+ * Class for a type.
+ *
+ * @param type type of the generated type
+ * @return class model of the new type if any
+ */
+ Optional type(TypeName type);
+
+ /**
+ * Create a descriptor type for a service.
+ *
+ * @param serviceType type of the service
+ * @return type of the service descriptor to be generated
+ */
+ TypeName descriptorType(TypeName serviceType);
+
+ /**
+ * All newly generated types.
+ *
+ * @return list of types and their source class model
+ */
+ List types();
+
+ /**
+ * All newly generated descriptors.
+ *
+ * @return list of descriptors and their source class model
+ */
+ List descriptors();
+
+ /**
+ * This provides support for replacements of types.
+ *
+ * @param typeName type name as required by the dependency ("injection point")
+ * @param valueSource code with the source of the parameter as Helidon provides it (such as Supplier of type)
+ * @return assignment to use for this instance, what type to use in Helidon registry, and code generator to transform to
+ * desired type
+ */
+ Assignment assignment(TypeName typeName, String valueSource);
+
+ /**
+ * Assignment for code generation. The original intended purpose is to support {@code Provider} from javax and jakarta
+ * without a dependency (or need to understand it) in the generator code.
+ *
+ * @param usedType type to use as the dependency type using only Helidon supported types
+ * (i.e. {@link java.util.function.Supplier} instead of jakarta {@code Provider}
+ * @param codeGenerator code generator that creates appropriate type required by the target
+ */
+ record Assignment(TypeName usedType, Consumer> codeGenerator) {
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContextImpl.java b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContextImpl.java
new file mode 100644
index 00000000000..ad7cfda1a0e
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenContextImpl.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import io.helidon.codegen.ClassCode;
+import io.helidon.codegen.CodegenContext;
+import io.helidon.codegen.CodegenContextDelegate;
+import io.helidon.codegen.classmodel.ClassModel;
+import io.helidon.common.types.TypeName;
+
+class RegistryCodegenContextImpl extends CodegenContextDelegate implements RegistryCodegenContext {
+ private final List descriptors = new ArrayList<>();
+ private final List nonDescriptors = new ArrayList<>();
+
+ RegistryCodegenContextImpl(CodegenContext context) {
+ super(context);
+ }
+
+ @Override
+ public Optional descriptor(TypeName serviceType) {
+ Objects.requireNonNull(serviceType);
+
+ for (DescriptorClassCode descriptor : descriptors) {
+ ClassCode classCode = descriptor.classCode();
+ if (classCode.mainTrigger().equals(serviceType)) {
+ return Optional.of(classCode.classModel());
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public void addDescriptor(String registryType,
+ TypeName serviceType,
+ TypeName descriptorType,
+ ClassModel.Builder descriptor,
+ double weight,
+ Set contracts,
+ Object... originatingElements) {
+ Objects.requireNonNull(registryType);
+ Objects.requireNonNull(serviceType);
+ Objects.requireNonNull(descriptorType);
+ Objects.requireNonNull(descriptor);
+ Objects.requireNonNull(contracts);
+ Objects.requireNonNull(originatingElements);
+
+ descriptors.add(new DescriptorClassCodeImpl(new ClassCode(descriptorType, descriptor, serviceType, originatingElements),
+ registryType,
+ weight,
+ contracts));
+ }
+
+ @Override
+ public void addType(TypeName type, ClassModel.Builder newClass, TypeName mainTrigger, Object... originatingElements) {
+ nonDescriptors.add(new ClassCode(type, newClass, mainTrigger, originatingElements));
+ }
+
+ @Override
+ public Optional type(TypeName type) {
+ for (ClassCode classCode : nonDescriptors) {
+ if (classCode.newType().equals(type)) {
+ return Optional.of(classCode.classModel());
+ }
+ }
+ for (DescriptorClassCode descriptor : descriptors) {
+ ClassCode classCode = descriptor.classCode();
+ if (classCode.newType().equals(type)) {
+ return Optional.of(classCode.classModel());
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public TypeName descriptorType(TypeName serviceType) {
+ // type is generated in the same package with a name suffix
+
+ return TypeName.builder()
+ .packageName(serviceType.packageName())
+ .className(descriptorClassName(serviceType))
+ .build();
+ }
+
+ @Override
+ public List types() {
+ return nonDescriptors;
+ }
+
+ @Override
+ public List descriptors() {
+ return descriptors;
+ }
+
+ @Override
+ public Assignment assignment(TypeName typeName, String valueSource) {
+ return new Assignment(typeName, it -> it.addContent(valueSource));
+ }
+
+ private static String descriptorClassName(TypeName typeName) {
+ // for MyType.MyService -> MyType_MyService__ServiceDescriptor
+
+ List enclosing = typeName.enclosingNames();
+ String namePrefix;
+ if (enclosing.isEmpty()) {
+ namePrefix = "";
+ } else {
+ namePrefix = String.join("_", enclosing) + "_";
+ }
+ return namePrefix
+ + typeName.className()
+ + "__ServiceDescriptor";
+ }
+
+}
diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Tool.java b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenExtension.java
similarity index 56%
rename from examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Tool.java
rename to service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenExtension.java
index 5c72bee20d4..5414ed3cd00 100644
--- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Tool.java
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryCodegenExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2023, 2024 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.
@@ -14,21 +14,22 @@
* limitations under the License.
*/
-package io.helidon.examples.inject.basics;
-
-import io.helidon.inject.api.Contract;
+package io.helidon.service.codegen;
/**
- * An example Tool interface contract.
+ * Code generation extension for Helidon Service REgistry.
*/
-@Contract
-public interface Tool {
-
+interface RegistryCodegenExtension {
/**
- * The name of the tool.
+ * Process a single round.
*
- * @return name of the tool
+ * @param roundContext round context
*/
- String name();
+ void process(RegistryRoundContext roundContext);
+ /**
+ * Called when the processing is over, and there will not be an additional processing round.
+ */
+ default void processingOver() {
+ }
}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/RegistryRoundContext.java b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryRoundContext.java
new file mode 100644
index 00000000000..b7d50143182
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/RegistryRoundContext.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.Collection;
+
+import io.helidon.common.types.TypeInfo;
+import io.helidon.common.types.TypeName;
+import io.helidon.common.types.TypedElementInfo;
+
+/**
+ * Context of a single round of code generation.
+ * For example the first round may generate types, that require additional code generation.
+ */
+interface RegistryRoundContext {
+ /**
+ * Available annotations for this provider.
+ *
+ * @return annotation types
+ */
+ Collection availableAnnotations();
+
+ /**
+ * All types for processing in this round.
+ *
+ * @return all type infos
+ */
+
+ Collection types();
+
+ /**
+ * All types annotated with a specific annotation.
+ *
+ * @param annotationType annotation type
+ * @return type infos annotated with the provided annotation
+ */
+
+ Collection annotatedTypes(TypeName annotationType);
+
+ /**
+ * All elements annotated with a specific annotation.
+ *
+ * @param annotationType annotation type
+ * @return elements annotated with the provided annotation
+ */
+ Collection annotatedElements(TypeName annotationType);
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/RoundContextImpl.java b/service/codegen/src/main/java/io/helidon/service/codegen/RoundContextImpl.java
new file mode 100644
index 00000000000..7e1c6bf8a7d
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/RoundContextImpl.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import io.helidon.common.types.TypeInfo;
+import io.helidon.common.types.TypeName;
+import io.helidon.common.types.TypedElementInfo;
+
+class RoundContextImpl implements RegistryRoundContext {
+ private final Map> annotationToTypes;
+ private final List types;
+ private final Collection annotations;
+
+ RoundContextImpl(Set annotations,
+ Map> annotationToTypes,
+ List types) {
+
+ this.annotations = annotations;
+ this.annotationToTypes = annotationToTypes;
+ this.types = types;
+ }
+
+ @Override
+ public Collection availableAnnotations() {
+ return annotations;
+ }
+
+ @Override
+ public Collection types() {
+ return types;
+ }
+
+ @Override
+ public Collection annotatedElements(TypeName annotationType) {
+ List typeInfos = annotationToTypes.get(annotationType);
+ if (typeInfos == null) {
+ return Set.of();
+ }
+
+ List result = new ArrayList<>();
+
+ for (TypeInfo typeInfo : typeInfos) {
+ typeInfo.elementInfo()
+ .stream()
+ .filter(it -> it.hasAnnotation(annotationType))
+ .forEach(result::add);
+ }
+
+ return result;
+ }
+
+ @Override
+ public Collection annotatedTypes(TypeName annotationType) {
+ List typeInfos = annotationToTypes.get(annotationType);
+ if (typeInfos == null) {
+ return Set.of();
+ }
+
+ List result = new ArrayList<>();
+
+ for (TypeInfo typeInfo : typeInfos) {
+ if (typeInfo.hasAnnotation(annotationType)) {
+ result.add(typeInfo);
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/ServiceCodegenTypes.java b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceCodegenTypes.java
new file mode 100644
index 00000000000..785d9567cf0
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceCodegenTypes.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import io.helidon.common.types.TypeName;
+
+/**
+ * Types used in code generation of Helidon Service.
+ */
+public final class ServiceCodegenTypes {
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.Service.Provider}.
+ */
+ public static final TypeName SERVICE_ANNOTATION_PROVIDER = TypeName.create("io.helidon.service.registry.Service.Provider");
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.Service.Contract}.
+ */
+ public static final TypeName SERVICE_ANNOTATION_CONTRACT = TypeName.create("io.helidon.service.registry.Service.Contract");
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.Service.ExternalContracts}.
+ */
+ public static final TypeName SERVICE_ANNOTATION_EXTERNAL_CONTRACTS = TypeName.create("io.helidon.service.registry.Service"
+ + ".ExternalContracts");
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.Service.Descriptor}.
+ */
+ public static final TypeName SERVICE_ANNOTATION_DESCRIPTOR =
+ TypeName.create("io.helidon.service.registry.Service.Descriptor");
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.GeneratedService.Descriptor}.
+ */
+ public static final TypeName SERVICE_DESCRIPTOR = TypeName.create("io.helidon.service.registry.GeneratedService.Descriptor");
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.Dependency}.
+ */
+ public static final TypeName SERVICE_DEPENDENCY = TypeName.create("io.helidon.service.registry.Dependency");
+ /**
+ * {@link io.helidon.common.types.TypeName} for {@code io.helidon.service.registry.DependencyContext}.
+ */
+ public static final TypeName SERVICE_DEPENDENCY_CONTEXT = TypeName.create("io.helidon.service.registry.DependencyContext");
+
+ private ServiceCodegenTypes() {
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/ServiceExtension.java b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceExtension.java
new file mode 100644
index 00000000000..7328c4112aa
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceExtension.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.Collection;
+
+import io.helidon.common.types.TypeInfo;
+import io.helidon.common.types.TypeName;
+
+class ServiceExtension implements RegistryCodegenExtension {
+ private static final TypeName GENERATOR = TypeName.create(ServiceExtension.class);
+
+ private final RegistryCodegenContext ctx;
+
+ ServiceExtension(RegistryCodegenContext codegenContext) {
+ this.ctx = codegenContext;
+ }
+
+ @Override
+ public void process(RegistryRoundContext roundContext) {
+ Collection descriptorsRequired = roundContext.types();
+
+ for (TypeInfo typeInfo : descriptorsRequired) {
+ generateDescriptor(descriptorsRequired, typeInfo);
+ }
+ }
+
+ private void generateDescriptor(Collection services,
+ TypeInfo typeInfo) {
+ GenerateServiceDescriptor.generate(GENERATOR, ctx, services, typeInfo);
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/ServiceOptions.java b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceOptions.java
new file mode 100644
index 00000000000..dae3314baf4
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceOptions.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import io.helidon.codegen.Option;
+
+/**
+ * Supported options specific to Helidon Service Registry.
+ */
+final class ServiceOptions {
+ /**
+ * Treat all super types as a contract for a given service type being added.
+ */
+ public static final Option AUTO_ADD_NON_CONTRACT_INTERFACES =
+ Option.create("helidon.registry.autoAddNonContractInterfaces",
+ "Treat all super types as a contract for a given service type being added.",
+ false);
+
+ private ServiceOptions() {
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenExtension.java b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenExtension.java
new file mode 100644
index 00000000000..0b60de1a369
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenExtension.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import io.helidon.codegen.ClassCode;
+import io.helidon.codegen.CodegenContext;
+import io.helidon.codegen.CodegenFiler;
+import io.helidon.codegen.CodegenOptions;
+import io.helidon.codegen.ModuleInfo;
+import io.helidon.codegen.classmodel.ClassModel;
+import io.helidon.codegen.spi.CodegenExtension;
+import io.helidon.common.Weighted;
+import io.helidon.common.types.Annotation;
+import io.helidon.common.types.TypeInfo;
+import io.helidon.common.types.TypeName;
+import io.helidon.common.types.TypeNames;
+import io.helidon.common.types.TypedElementInfo;
+import io.helidon.service.codegen.HelidonMetaInfServices.DescriptorMeta;
+
+import static io.helidon.service.codegen.ServiceCodegenTypes.SERVICE_ANNOTATION_DESCRIPTOR;
+
+/**
+ * Handles processing of all extensions, creates context and writes types.
+ */
+class ServiceRegistryCodegenExtension implements CodegenExtension {
+ private static final TypeName TYPE = TypeName.create(ServiceRegistryCodegenExtension.class);
+
+ private final Map> typeToExtensions = new HashMap<>();
+ private final Map> extensionPredicates = new IdentityHashMap<>();
+ private final Set generatedServiceDescriptors = new HashSet<>();
+ private final TypeName generator;
+ private final RegistryCodegenContext ctx;
+ private final List extensions;
+ private final String module;
+
+ private ServiceRegistryCodegenExtension(CodegenContext ctx, TypeName generator) {
+ this.ctx = RegistryCodegenContext.create(ctx);
+ this.generator = generator;
+ this.module = ctx.moduleName().orElse(null);
+
+ ServiceExtension serviceExtension = new ServiceExtension(this.ctx);
+ this.extensions = List.of(serviceExtension);
+ this.typeToExtensions.put(ServiceCodegenTypes.SERVICE_ANNOTATION_PROVIDER, List.of(serviceExtension));
+ }
+
+ static ServiceRegistryCodegenExtension create(CodegenContext ctx, TypeName generator) {
+ return new ServiceRegistryCodegenExtension(ctx, generator);
+ }
+
+ @Override
+ public void process(io.helidon.codegen.RoundContext roundContext) {
+ Collection allTypes = roundContext.types();
+ if (allTypes.isEmpty()) {
+ extensions.forEach(it -> it.process(createRoundContext(List.of(), it)));
+ return;
+ }
+
+ // type info list will contain all mapped annotations, so this is the state we can do annotation processing on
+ List annotatedTypes = annotatedTypes(allTypes);
+
+ // and now for each extension, we discover types that contain annotations supported by that extension
+ // and create a new round context for each extension
+
+ // for each extension, create a RoundContext with just the stuff it wants
+ for (RegistryCodegenExtension extension : extensions) {
+ extension.process(createRoundContext(annotatedTypes, extension));
+ }
+
+ writeNewTypes();
+
+ for (TypeInfo typeInfo : roundContext.annotatedTypes(SERVICE_ANNOTATION_DESCRIPTOR)) {
+ // add each declared descriptor in source code
+ Annotation descriptorAnnot = typeInfo.annotation(SERVICE_ANNOTATION_DESCRIPTOR);
+
+ double weight = descriptorAnnot.doubleValue("weight").orElse(Weighted.DEFAULT_WEIGHT);
+ Set contracts = descriptorAnnot.typeValues("contracts")
+ .map(Set::copyOf)
+ .orElseGet(Set::of);
+
+ String registryType = descriptorAnnot.stringValue("registryType").orElse("core");
+
+ // predefined service descriptor
+ generatedServiceDescriptors.add(new DescriptorMeta(registryType,
+ typeInfo.typeName(),
+ weight,
+ contracts));
+ }
+
+ if (roundContext.availableAnnotations().size() == 1 && roundContext.availableAnnotations()
+ .contains(TypeNames.GENERATED)) {
+
+ // no other types generated by Helidon annotation processors, we can generate module component (unless already done)
+ if (!generatedServiceDescriptors.isEmpty()) {
+ addDescriptorsToServiceMeta();
+ generatedServiceDescriptors.clear();
+ }
+ }
+ }
+
+ @Override
+ public void processingOver(io.helidon.codegen.RoundContext roundContext) {
+ // do processing over in each extension
+ extensions.forEach(RegistryCodegenExtension::processingOver);
+
+ // if there was any type generated, write it out (will not trigger next round)
+ writeNewTypes();
+
+ if (!generatedServiceDescriptors.isEmpty()) {
+ // re-check, maybe we run from a tool that does not generate anything except for the module component,
+ // so let's create it now anyway (if created above, the set of descriptors is empty, so it is not attempted again
+ // if somebody adds a service descriptor when processingOver, than it is wrong anyway
+ addDescriptorsToServiceMeta();
+ generatedServiceDescriptors.clear();
+ }
+ }
+
+ private void addDescriptorsToServiceMeta() {
+ // and write the module component
+ Optional currentModule = ctx.module();
+
+ // generate module
+ String moduleName = this.module == null ? currentModule.map(ModuleInfo::name).orElse(null) : module;
+ String packageName = CodegenOptions.CODEGEN_PACKAGE.findValue(ctx.options())
+ .orElseGet(() -> topLevelPackage(generatedServiceDescriptors));
+ boolean hasModule = moduleName != null && !"unnamed module".equals(moduleName);
+ if (!hasModule) {
+ moduleName = "unnamed/" + packageName + (ctx.scope().isProduction() ? "" : "/" + ctx.scope().name());
+ }
+ HelidonMetaInfServices services = HelidonMetaInfServices.create(ctx.filer(),
+ TYPE,
+ TYPE,
+ moduleName);
+
+ services.addAll(generatedServiceDescriptors);
+ services.write();
+ }
+
+ private void writeNewTypes() {
+ // after each round, write all generated types
+ CodegenFiler filer = ctx.filer();
+
+ // generate all code
+ var descriptors = ctx.descriptors();
+ for (var descriptor : descriptors) {
+ ClassCode classCode = descriptor.classCode();
+ ClassModel classModel = classCode.classModel().build();
+ generatedServiceDescriptors.add(new DescriptorMeta(descriptor.registryType(),
+ classCode.newType(),
+ descriptor.weight(),
+ descriptor.contracts()));
+ filer.writeSourceFile(classModel, classCode.originatingElements());
+ }
+ descriptors.clear();
+
+ var otherTypes = ctx.types();
+ for (var classCode : otherTypes) {
+ ClassModel classModel = classCode.classModel().build();
+ filer.writeSourceFile(classModel, classCode.originatingElements());
+ }
+ otherTypes.clear();
+ }
+
+ private List annotatedTypes(Collection allTypes) {
+ List result = new ArrayList<>();
+
+ for (TypeInfo typeInfo : allTypes) {
+ result.add(new TypeInfoAndAnnotations(typeInfo, annotations(typeInfo)));
+ }
+ return result;
+ }
+
+ private RegistryRoundContext createRoundContext(List annotatedTypes,
+ RegistryCodegenExtension extension) {
+ Set extAnnots = new HashSet<>();
+ Map> extAnnotToType = new HashMap<>();
+ Map extTypes = new HashMap<>();
+
+ for (TypeInfoAndAnnotations annotatedType : annotatedTypes) {
+ for (TypeName typeName : annotatedType.annotations()) {
+ boolean added = false;
+ List validExts = this.typeToExtensions.get(typeName);
+ if (validExts != null) {
+ for (RegistryCodegenExtension validExt : validExts) {
+ if (validExt == extension) {
+ extAnnots.add(typeName);
+ extAnnotToType.computeIfAbsent(typeName, key -> new ArrayList<>())
+ .add(annotatedType.typeInfo());
+ extTypes.put(annotatedType.typeInfo().typeName(), annotatedType.typeInfo);
+ added = true;
+ }
+ }
+ }
+ if (!added) {
+ Predicate predicate = this.extensionPredicates.get(extension);
+ if (predicate != null && predicate.test(typeName)) {
+ extAnnots.add(typeName);
+ extAnnotToType.computeIfAbsent(typeName, key -> new ArrayList<>())
+ .add(annotatedType.typeInfo());
+ extTypes.put(annotatedType.typeInfo().typeName(), annotatedType.typeInfo);
+ }
+ }
+ }
+ }
+
+ return new RoundContextImpl(
+ Set.copyOf(extAnnots),
+ Map.copyOf(extAnnotToType),
+ List.copyOf(extTypes.values()));
+ }
+
+ private Set annotations(TypeInfo theTypeInfo) {
+ Set result = new HashSet<>();
+
+ // on type
+ theTypeInfo.annotations()
+ .stream()
+ .map(Annotation::typeName)
+ .forEach(result::add);
+
+ // on fields, methods etc.
+ theTypeInfo.elementInfo()
+ .stream()
+ .map(TypedElementInfo::annotations)
+ .flatMap(List::stream)
+ .map(Annotation::typeName)
+ .forEach(result::add);
+
+ // on parameters
+ theTypeInfo.elementInfo()
+ .stream()
+ .map(TypedElementInfo::parameterArguments)
+ .flatMap(List::stream)
+ .map(TypedElementInfo::annotations)
+ .flatMap(List::stream)
+ .map(Annotation::typeName)
+ .forEach(result::add);
+
+ return result;
+ }
+
+ private String topLevelPackage(Set typeNames) {
+ String thePackage = typeNames.iterator().next().descriptor().packageName();
+
+ for (DescriptorMeta typeName : typeNames) {
+ String nextPackage = typeName.descriptor().packageName();
+ if (nextPackage.length() < thePackage.length()) {
+ thePackage = nextPackage;
+ }
+ }
+
+ return thePackage;
+ }
+
+ private record TypeInfoAndAnnotations(TypeInfo typeInfo, Set annotations) {
+ }
+}
diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenProvider.java b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenProvider.java
new file mode 100644
index 00000000000..de39791ea3d
--- /dev/null
+++ b/service/codegen/src/main/java/io/helidon/service/codegen/ServiceRegistryCodegenProvider.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023, 2024 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.service.codegen;
+
+import java.util.Set;
+
+import io.helidon.codegen.CodegenContext;
+import io.helidon.codegen.Option;
+import io.helidon.codegen.spi.CodegenExtension;
+import io.helidon.codegen.spi.CodegenExtensionProvider;
+import io.helidon.common.types.TypeName;
+import io.helidon.common.types.TypeNames;
+
+/**
+ * A {@link java.util.ServiceLoader} provider implementation for {@link io.helidon.codegen.spi.CodegenExtensionProvider}
+ * that handles Helidon Service Registry code generation.
+ */
+public class ServiceRegistryCodegenProvider implements CodegenExtensionProvider {
+ private static final Set