From b671795830e283bc5edecde47c31ced883f51175 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Mon, 13 May 2024 09:33:23 -0400 Subject: [PATCH 001/245] Proposal for a new annotation @ExecuteOn (#8643) * Proposal for a new annotation @AsyncPlatform to run any CDI bean method on a separate platform thread and always blocking the calling thread. See tests. * Moves system property to pom.xml Signed-off-by: Santiago Pericas-Geertsen * Use AnnotatedMethod to lookup annotations in interceptor. Signed-off-by: Santiago Pericas-Geertsen * Adds support to configure executor. Updated tests. Signed-off-by: Santiago Pericas-Geertsen * Renames annotation to OnNewThread and adds support for a thread type. Updates tests. * Adds support for new EXECUTOR thread type. Signed-off-by: Santiago Pericas-Geertsen * Updates tests to make them easier to understand. Signed-off-by: Santiago Pericas-Geertsen * Use mp instead of helidon to root config for annotation. Signed-off-by: Santiago Pericas-Geertsen * Additional tests to show context propagation. Signed-off-by: Santiago Pericas-Geertsen * Renamed to RunOnThread. Separated config for virtual and platform. * Renames to ExecuteOn. Separates config for virtual and platform, and uses execute-on as root key. --------- Signed-off-by: Santiago Pericas-Geertsen --- microprofile/cdi/pom.xml | 20 +++ .../helidon/microprofile/cdi/ExecuteOn.java | 90 +++++++++++ .../microprofile/cdi/ExecuteOnExtension.java | 96 ++++++++++++ .../cdi/ExecuteOnInterceptor.java | 111 ++++++++++++++ .../cdi/src/main/java/module-info.java | 6 +- .../microprofile/cdi/ExecuteOnTest.java | 140 ++++++++++++++++++ .../cdi/src/test/resources/application.yaml | 24 +++ 7 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOn.java create mode 100644 microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnExtension.java create mode 100644 microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnInterceptor.java create mode 100644 microprofile/cdi/src/test/java/io/helidon/microprofile/cdi/ExecuteOnTest.java create mode 100644 microprofile/cdi/src/test/resources/application.yaml diff --git a/microprofile/cdi/pom.xml b/microprofile/cdi/pom.xml index b23501d1b3a..41271961d02 100644 --- a/microprofile/cdi/pom.xml +++ b/microprofile/cdi/pom.xml @@ -77,6 +77,14 @@ io.helidon.common.features helidon-common-features + + io.helidon.common + helidon-common-configurable + + + jakarta.enterprise + jakarta.enterprise.cdi-api + org.junit.jupiter junit-jupiter-api @@ -104,6 +112,18 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + false + + true + + + + diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOn.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOn.java new file mode 100644 index 00000000000..eb08d44c41c --- /dev/null +++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOn.java @@ -0,0 +1,90 @@ +/* + * 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.cdi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +import jakarta.enterprise.util.Nonbinding; +import jakarta.interceptor.InterceptorBinding; + +/** + * Annotates a CDI bean method that shall be executed on a new thread. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +@Inherited +@InterceptorBinding +public @interface ExecuteOn { + + /** + * Type of thread to use for invocation. + */ + enum ThreadType { + /** + * A thread of type platform (non-virtual). + */ + PLATFORM, + + /** + * A thread of type virtual. + */ + VIRTUAL, + + /** + * A named executor lookup using CDI. + */ + EXECUTOR + } + + /** + * Thread type for invocation. + * + * @return thread type + */ + @Nonbinding + ThreadType value() default ThreadType.PLATFORM; + + /** + * Waiting timeout. + * + * @return waiting timeout + */ + @Nonbinding + long timeout() default 10000L; + + /** + * Waiting time unit. + * + * @return waiting time unit + */ + @Nonbinding + TimeUnit unit() default TimeUnit.MILLISECONDS; + + /** + * Name of executor when {@link ThreadType#EXECUTOR} is selected. + * + * @return executor name + */ + @Nonbinding + String executorName() default ""; +} diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnExtension.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnExtension.java new file mode 100644 index 00000000000..2ef68aab964 --- /dev/null +++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnExtension.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.microprofile.cdi; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; + +import io.helidon.common.LazyValue; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.literal.NamedLiteral; +import jakarta.enterprise.inject.spi.AnnotatedMethod; +import jakarta.enterprise.inject.spi.AnnotatedType; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; +import jakarta.enterprise.inject.spi.Extension; +import jakarta.enterprise.inject.spi.ProcessManagedBean; +import jakarta.enterprise.inject.spi.ProcessSyntheticBean; + +/** + * CDI extension to support the {@link ExecuteOn} annotation. + */ +public class ExecuteOnExtension implements Extension { + + private final LazyValue>> methodMap = LazyValue.create(ConcurrentHashMap::new); + + void registerMethods(BeanManager bm, @Observes ProcessSyntheticBean event) { + registerMethods(bm.createAnnotatedType(event.getBean().getBeanClass())); + } + + void registerMethods(@Observes ProcessManagedBean event) { + registerMethods(event.getAnnotatedBeanClass()); + } + + private void registerMethods(AnnotatedType type) { + for (AnnotatedMethod annotatedMethod : type.getMethods()) { + if (annotatedMethod.isAnnotationPresent(ExecuteOn.class)) { + methodMap.get().put(annotatedMethod.getJavaMember(), annotatedMethod); + } + } + } + + void validateAnnotations(BeanManager bm, @Observes @Initialized(ApplicationScoped.class) Object event) { + methodMap.get().forEach((method, annotatedMethod) -> validateExecutor(bm, annotatedMethod)); + } + + private static void validateExecutor(BeanManager bm, AnnotatedMethod method) { + ExecuteOn executeOn = method.getAnnotation(ExecuteOn.class); + if (executeOn.value() == ExecuteOn.ThreadType.EXECUTOR) { + String executorName = executeOn.executorName(); + Set> beans = bm.getBeans(ExecutorService.class, NamedLiteral.of(executorName)); + if (beans.isEmpty()) { + throw new IllegalArgumentException("Unable to resolve named executor service '" + + executeOn.value() + "' at " + + method.getJavaMember().getDeclaringClass().getName() + "::" + + method.getJavaMember().getName()); + } + } + } + + ExecuteOn getAnnotation(Method method) { + AnnotatedMethod annotatedMethod = methodMap.get().get(method); + if (annotatedMethod != null) { + return annotatedMethod.getAnnotation(ExecuteOn.class); + } + throw new IllegalArgumentException("Unable to map method " + method); + } + + void registerInterceptors(@Observes BeforeBeanDiscovery discovery, BeanManager bm) { + discovery.addAnnotatedType(bm.createAnnotatedType(ExecuteOnInterceptor.class), + ExecuteOnInterceptor.class.getName()); + } + + void clearMethodMap() { + methodMap.get().clear(); + } +} diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnInterceptor.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnInterceptor.java new file mode 100644 index 00000000000..a18cca3901f --- /dev/null +++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/ExecuteOnInterceptor.java @@ -0,0 +1,111 @@ +/* + * 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.cdi; + +import java.util.concurrent.ExecutorService; + +import io.helidon.common.LazyValue; +import io.helidon.common.configurable.ThreadPoolSupplier; +import io.helidon.config.mp.MpConfig; + +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.literal.NamedLiteral; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.CDI; +import jakarta.inject.Inject; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InvocationContext; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; + +/** + * Intercepts calls to bean methods to be executed on a new thread. + */ +@Interceptor +@ExecuteOn +@Priority(Interceptor.Priority.PLATFORM_AFTER + 10) +class ExecuteOnInterceptor { + + private static final String EXECUTE_ON = "execute-on"; + private static final String RUN_ON_VIRTUAL_THREAD = EXECUTE_ON + ".virtual"; + private static final String RUN_ON_PLATFORM_THREAD = EXECUTE_ON + ".platform"; + + private static final LazyValue PLATFORM_EXECUTOR_SERVICE + = LazyValue.create(() -> { + Config mpConfig = ConfigProvider.getConfig(); + io.helidon.config.Config config = MpConfig.toHelidonConfig(mpConfig); + return ThreadPoolSupplier.builder() + .threadNamePrefix(EXECUTE_ON) + .config(config.get(RUN_ON_PLATFORM_THREAD)) + .virtualThreads(false) // overrides to platform threads + .build() + .get(); + }); + + private static final LazyValue VIRTUAL_EXECUTOR_SERVICE + = LazyValue.create(() -> { + Config mpConfig = ConfigProvider.getConfig(); + io.helidon.config.Config config = MpConfig.toHelidonConfig(mpConfig); + String threadNamePrefix = config.get(RUN_ON_VIRTUAL_THREAD) + .get("thread-name-prefix") + .asString() + .asOptional() + .orElse(EXECUTE_ON); + return ThreadPoolSupplier.builder() + .threadNamePrefix(threadNamePrefix) + .virtualThreads(true) + .build() + .get(); + }); + + @Inject + private ExecuteOnExtension extension; + + /** + * Intercepts a call to bean method annotated by {@code @OnNewThread}. + * + * @param context Invocation context. + * @return Whatever the intercepted method returns. + * @throws Throwable If a problem occurs. + */ + @AroundInvoke + public Object executeOn(InvocationContext context) throws Throwable { + ExecuteOn executeOn = extension.getAnnotation(context.getMethod()); + return switch (executeOn.value()) { + case PLATFORM -> PLATFORM_EXECUTOR_SERVICE.get() + .submit(context::proceed) + .get(executeOn.timeout(), executeOn.unit()); + case VIRTUAL -> VIRTUAL_EXECUTOR_SERVICE.get() + .submit(context::proceed) + .get(executeOn.timeout(), executeOn.unit()); + case EXECUTOR -> findExecutor(executeOn.executorName()) + .submit(context::proceed) + .get(executeOn.timeout(), executeOn.unit()); + }; + } + + /** + * Find executor by name. Validation in {@link ExecuteOnExtension#validateAnnotations(BeanManager, Object)}. + * + * @param executorName name of executor + * @return executor instance looked up via CDI + */ + private static ExecutorService findExecutor(String executorName) { + return CDI.current().select(ExecutorService.class, NamedLiteral.of(executorName)).get(); + } +} diff --git a/microprofile/cdi/src/main/java/module-info.java b/microprofile/cdi/src/main/java/module-info.java index 78404a5b0bf..e996197c76d 100644 --- a/microprofile/cdi/src/main/java/module-info.java +++ b/microprofile/cdi/src/main/java/module-info.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,6 +33,7 @@ requires io.helidon.common.context; requires io.helidon.common.features.api; requires io.helidon.common.features; + requires io.helidon.common.configurable; requires io.helidon.common; requires io.helidon.config.mp; requires io.helidon.config; @@ -69,6 +70,9 @@ provides org.jboss.weld.bootstrap.api.Service with io.helidon.microprofile.cdi.ExecutorServices; + provides jakarta.enterprise.inject.spi.Extension + with io.helidon.microprofile.cdi.ExecuteOnExtension; + opens io.helidon.microprofile.cdi to weld.core.impl; } \ No newline at end of file diff --git a/microprofile/cdi/src/test/java/io/helidon/microprofile/cdi/ExecuteOnTest.java b/microprofile/cdi/src/test/java/io/helidon/microprofile/cdi/ExecuteOnTest.java new file mode 100644 index 00000000000..59988d97ff9 --- /dev/null +++ b/microprofile/cdi/src/test/java/io/helidon/microprofile/cdi/ExecuteOnTest.java @@ -0,0 +1,140 @@ +/* + * 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.cdi; + +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import io.helidon.common.context.Context; +import io.helidon.common.context.Contexts; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; +import jakarta.enterprise.inject.spi.CDI; +import jakarta.inject.Named; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static io.helidon.microprofile.cdi.ExecuteOn.ThreadType; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +class ExecuteOnTest { + + static SeContainer seContainer; + static OnNewThreadBean bean; + + @BeforeAll + @SuppressWarnings("unchecked") + static void startCdi() { + seContainer = SeContainerInitializer.newInstance() + .disableDiscovery() + .addExtensions(ExecuteOnExtension.class) + .addBeanClasses(OnNewThreadBean.class) + .initialize(); + bean = CDI.current().select(OnNewThreadBean.class).get(); + } + + @AfterAll + static void stopCdi() { + seContainer.close(); + } + + static class OnNewThreadBean { + + @ExecuteOn(ThreadType.PLATFORM) + Thread cpuIntensive() { + return Thread.currentThread(); + } + + @ExecuteOn(value = ThreadType.PLATFORM, timeout = 10000) + Thread evenMoreCpuIntensive() { + return Thread.currentThread(); + } + + @ExecuteOn(ThreadType.VIRTUAL) + Thread onVirtualThread() { + return Thread.currentThread(); + } + + @ExecuteOn(value = ThreadType.EXECUTOR, executorName = "my-executor") + Thread onMyExecutor() { + return Thread.currentThread(); + } + + @ExecuteOn(ThreadType.VIRTUAL) + Optional verifyContextVirtual() { + return Contexts.context().flatMap(context -> context.get("hello", String.class)); + } + + @ExecuteOn(ThreadType.PLATFORM) + Optional verifyContextPlatform() { + return Contexts.context().flatMap(context -> context.get("hello", String.class)); + } + + @Produces + @Named("my-executor") + ExecutorService myExecutor() { + return Executors.newFixedThreadPool(2); + } + } + + @Test + void cpuIntensiveTest() { + Thread thread = bean.cpuIntensive(); + assertThat(thread.isVirtual(), is(false)); + assertThat(thread.getName().startsWith("my-platform-thread"), is(true)); + } + + @Test + void evenMoreCpuIntensiveTest() { + Thread thread = bean.evenMoreCpuIntensive(); + assertThat(thread.isVirtual(), is(false)); + assertThat(thread.getName().startsWith("my-platform-thread"), is(true)); + } + + @Test + void onVirtualThread() { + Thread thread = bean.onVirtualThread(); + assertThat(thread.isVirtual(), is(true)); + assertThat(thread.getName().startsWith("my-virtual-thread"), is(true)); + } + + @Test + void onMyExecutor() { + Thread thread = bean.onMyExecutor(); + assertThat(thread.isVirtual(), is(false)); + assertThat(thread.getName().startsWith("pool"), is(true)); + } + + @Test + void verifyContextVirtual() { + Context context = Contexts.globalContext(); + context.register("hello", "world"); + Optional optional = Contexts.runInContext(context, bean::verifyContextVirtual); + assertThat(optional.orElseThrow(), is("world")); + } + + @Test + void verifyContextPlatform() { + Context context = Contexts.globalContext(); + context.register("hello", "world"); + Optional optional = Contexts.runInContext(context, bean::verifyContextPlatform); + assertThat(optional.orElseThrow(), is("world")); + } +} diff --git a/microprofile/cdi/src/test/resources/application.yaml b/microprofile/cdi/src/test/resources/application.yaml new file mode 100644 index 00000000000..e17037de952 --- /dev/null +++ b/microprofile/cdi/src/test/resources/application.yaml @@ -0,0 +1,24 @@ +# +# 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. +# + +execute-on: + platform: + thread-name-prefix: "my-platform-thread" + core-pool-size: 1 + max-pool-size: 2 + queue-capacity: 10 + virtual: + thread-name-prefix: "my-virtual-thread" From 2185987e8f9310e1f043dfa0772582c8067a87f3 Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Mon, 13 May 2024 07:59:04 -0700 Subject: [PATCH 002/245] Test and fix for MP config bug 8737 (getOrdinal returning wrong value) (#8744) --- .../config/mp/MpEnvironmentVariablesSource.java | 15 +++++++++++++-- .../config/mp/MpSystemPropertiesSource.java | 15 +++++++++++++-- .../io/helidon/config/mp/MpConfigSourcesTest.java | 12 ++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) 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 90cf9a78820..eb1b0dd1daa 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 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. @@ -24,8 +24,9 @@ import jakarta.annotation.Priority; import org.eclipse.microprofile.config.spi.ConfigSource; -@Priority(300) +@Priority(MpEnvironmentVariablesSource.MY_DEFAULT_ORDINAL) 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 = "_"; @@ -70,6 +71,16 @@ public String getValue(String propertyName) { }).value; } + @Override + public int getOrdinal() { + String configOrdinal = getValue(CONFIG_ORDINAL); + if (configOrdinal == null) { + return MY_DEFAULT_ORDINAL; + } else { + return ConfigSource.super.getOrdinal(); + } + } + @Override public String getName() { return "Environment Variables"; diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesSource.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesSource.java index f9cf1d96489..881e142fd08 100644 --- a/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesSource.java +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpSystemPropertiesSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 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,9 @@ import jakarta.annotation.Priority; import org.eclipse.microprofile.config.spi.ConfigSource; -@Priority(400) +@Priority(MpSystemPropertiesSource.MY_DEFAULT_ORDINAL) class MpSystemPropertiesSource implements ConfigSource { + static final int MY_DEFAULT_ORDINAL = 400; private final Properties props; MpSystemPropertiesSource() { @@ -57,6 +58,16 @@ public String getName() { return "System Properties"; } + @Override + public int getOrdinal() { + String configOrdinal = getValue(CONFIG_ORDINAL); + if (configOrdinal == null) { + return MY_DEFAULT_ORDINAL; + } else { + return ConfigSource.super.getOrdinal(); + } + } + @Override public String toString() { return getName() + " (" + getOrdinal() + ")"; diff --git a/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java b/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java index 4bb42236b59..ac43156af3d 100644 --- a/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java +++ b/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java @@ -109,6 +109,18 @@ void testMpConfigSourcesNullConfig() { assertThat(npe.getMessage(), is("Config cannot be null")); } + @Test + void testSystemPropertiesConfigSourceDefaultOrdinal() { + org.eclipse.microprofile.config.spi.ConfigSource configSource = MpConfigSources.systemProperties(); + assertThat(configSource.getOrdinal(), is(400)); + } + + @Test + void testEnvironmentVariablesConfigSourceDefaultOrdinal() { + org.eclipse.microprofile.config.spi.ConfigSource configSource = MpConfigSources.environmentVariables(); + assertThat(configSource.getOrdinal(), is(300)); + } + @Test void testPropertiesMetaConfigProvider() { typeChecks("properties", """ From 0b98e23b609a817ee2226534e87d941dfd68bd21 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Mon, 13 May 2024 20:12:05 +0200 Subject: [PATCH 003/245] Make hibernate and native image compatible (#8740) Signed-off-by: tvallin --- dependencies/pom.xml | 5 ++ integrations/cdi/hibernate-cdi/pom.xml | 5 ++ .../cdi/hibernate/BytecodeProvider.java | 58 +++++++++++++++++++ .../hibernate/BytecodeProviderInitiator.java | 54 +++++++++++++++++ .../src/main/java/module-info.java | 3 +- .../native-image/reflection-config.json | 7 +++ 6 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProvider.java create mode 100644 integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProviderInitiator.java diff --git a/dependencies/pom.xml b/dependencies/pom.xml index efe38f1ef9d..03904a97473 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -929,6 +929,11 @@ nativeimage ${version.lib.graalvm} + + org.graalvm.nativeimage + svm + ${version.lib.graalvm} + com.graphql-java graphql-java diff --git a/integrations/cdi/hibernate-cdi/pom.xml b/integrations/cdi/hibernate-cdi/pom.xml index cadcc8e3aee..bad5ddbedf9 100644 --- a/integrations/cdi/hibernate-cdi/pom.xml +++ b/integrations/cdi/hibernate-cdi/pom.xml @@ -74,6 +74,11 @@ jakarta.transaction-api provided + + org.graalvm.nativeimage + svm + provided + diff --git a/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProvider.java b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProvider.java new file mode 100644 index 00000000000..0b2953a14d3 --- /dev/null +++ b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProvider.java @@ -0,0 +1,58 @@ +/* + * 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.hibernate; + +import java.util.Map; +import java.util.function.Predicate; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import org.hibernate.bytecode.spi.ReflectionOptimizer; +import org.hibernate.property.access.spi.PropertyAccess; + +/** + * Reflection optimizer of the no-op Bytecode provider cannot be disabled + * with properties and throw an exception, so it has to return {@code null}. + * After Hibernate version 6.5, this substitution is not required anymore. + */ +@TargetClass(className = "org.hibernate.bytecode.internal.none.BytecodeProviderImpl", + onlyWith = BytecodeProvider.SubstituteOnlyIfPresent.class) +final class BytecodeProvider { + + private BytecodeProvider() { + } + + @Substitute + @SuppressWarnings("NullAway") + public ReflectionOptimizer getReflectionOptimizer(Class clazz, Map propertyAccessMap) { + return null; + } + + static class SubstituteOnlyIfPresent implements Predicate { + + @Override + public boolean test(String type) { + try { + Class clazz = Class.forName(type, false, getClass().getClassLoader()); + clazz.getDeclaredMethod("getReflectionOptimizer", Class.class, Map.class); + return true; + } catch (ClassNotFoundException | NoClassDefFoundError | NoSuchMethodException ex) { + return false; + } + } + } +} diff --git a/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProviderInitiator.java b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProviderInitiator.java new file mode 100644 index 00000000000..5284cf1a2f2 --- /dev/null +++ b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/BytecodeProviderInitiator.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.cdi.hibernate; + +import java.util.function.Predicate; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import org.hibernate.bytecode.spi.BytecodeProvider; + +/** + * In native image, we force the usage of the no-op bytecode provider so no bytecode + * operation happen during runtime. + */ +@TargetClass(className = "org.hibernate.bytecode.internal.BytecodeProviderInitiator", + onlyWith = BytecodeProviderInitiator.SubstituteOnlyIfPresent.class) +final class BytecodeProviderInitiator { + + private BytecodeProviderInitiator() { + } + + @Substitute + public static BytecodeProvider buildBytecodeProvider(String providerName) { + return new org.hibernate.bytecode.internal.none.BytecodeProviderImpl(); + } + + static class SubstituteOnlyIfPresent implements Predicate { + + @Override + public boolean test(String type) { + try { + Class clazz = Class.forName(type, false, getClass().getClassLoader()); + clazz.getDeclaredMethod("buildBytecodeProvider", String.class); + return true; + } catch (ClassNotFoundException | NoClassDefFoundError | NoSuchMethodException ex) { + return false; + } + } + } +} diff --git a/integrations/cdi/hibernate-cdi/src/main/java/module-info.java b/integrations/cdi/hibernate-cdi/src/main/java/module-info.java index afddf2a74ba..6ba973ced31 100644 --- a/integrations/cdi/hibernate-cdi/src/main/java/module-info.java +++ b/integrations/cdi/hibernate-cdi/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. @@ -40,6 +40,7 @@ requires transitive jakarta.inject; requires jakarta.persistence; requires transitive jakarta.transaction; + requires org.graalvm.nativeimage; requires transitive org.hibernate.orm.core; requires static io.helidon.common.features.api; diff --git a/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json b/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json index 6877dcaa4ae..65de8253a76 100644 --- a/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json +++ b/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json @@ -102,6 +102,13 @@ "org.hibernate.hql.internal.ast.tree.ConstructorNode", "org.hibernate.hql.internal.ast.tree.LiteralNode", "org.hibernate.hql.internal.ast.tree.BinaryArithmeticOperatorNode", + "org.hibernate.query.hql.spi.DotIdentifierConsumer[]", + "org.hibernate.query.hql.spi.SqmCreationProcessingState[]", + "org.hibernate.query.sqm.spi.ParameterDeclarationContext[]", + "org.hibernate.query.sqm.sql.FromClauseIndex[]", + "org.hibernate.sql.ast.tree.select.QueryPart[]", + "org.hibernate.sql.ast.spi.SqlAstProcessingState[]", + "org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState[]", "antlr.CommonToken" ], "exclude": [ From 61bfe3ec6de4abcbd3fd05ced2f702eff3377c35 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Tue, 14 May 2024 19:21:30 +0200 Subject: [PATCH 004/245] Register additional hibernate classes for reflection (#8758) Signed-off-by: tvallin --- .../native-image/reflection-config.json | 2 ++ .../reflect-config.json | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json b/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json index 65de8253a76..dd41be59227 100644 --- a/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json +++ b/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/helidon/native-image/reflection-config.json @@ -106,8 +106,10 @@ "org.hibernate.query.hql.spi.SqmCreationProcessingState[]", "org.hibernate.query.sqm.spi.ParameterDeclarationContext[]", "org.hibernate.query.sqm.sql.FromClauseIndex[]", + "org.hibernate.sql.ast.tree.Statement[]", "org.hibernate.sql.ast.tree.select.QueryPart[]", "org.hibernate.sql.ast.spi.SqlAstProcessingState[]", + "org.hibernate.sql.results.graph.FetchParent[]", "org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState[]", "antlr.CommonToken" ], diff --git a/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/native-image/io.helidon.integrations.cdi/helidon-integrations-cdi-hibernate/reflect-config.json b/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/native-image/io.helidon.integrations.cdi/helidon-integrations-cdi-hibernate/reflect-config.json index 7d4b31bcc3c..43d0fdf81ba 100644 --- a/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/native-image/io.helidon.integrations.cdi/helidon-integrations-cdi-hibernate/reflect-config.json +++ b/integrations/cdi/hibernate-cdi/src/main/resources/META-INF/native-image/io.helidon.integrations.cdi/helidon-integrations-cdi-hibernate/reflect-config.json @@ -17,6 +17,23 @@ } ] }, + { + "name": "org.hibernate.boot.beanvalidation.TypeSafeActivator", + "methods": [ + { + "name" : "activate", + "parameterTypes" : [ + "org.hibernate.boot.beanvalidation.ActivationContext" + ] + }, + { + "name" : "validateSuppliedFactory", + "parameterTypes" : [ + "java.lang.Object" + ] + } + ] + }, { "name": "org.hibernate.boot.cfgxml.internal.CfgXmlAccessServiceImpl", "allPublicMethods": true @@ -30,6 +47,18 @@ } ] }, + { + "name": "org.hibernate.boot.model.relational.ColumnOrderingStrategy", + "allDeclaredMethods": true + }, + { + "name": "org.hibernate.boot.model.relational.ColumnOrderingStrategyLegacy", + "methods": [ + { + "name" : "" + } + ] + }, { "name": "org.hibernate.cache.internal.DisabledCaching", "allPublicMethods": true From 93cc1683c5ee1bf9d6a8f1171f5d8041e2d40614 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Wed, 15 May 2024 14:17:58 -0500 Subject: [PATCH 005/245] 4.x Associate tracer-level tags with Jaeger process level (instead of span level) (#8764) * Attach trace-level tags to the OpenTelemetry instance Signed-off-by: Tim Quinn * Slightly clean-up the pom --------- Signed-off-by: Tim Quinn --- tracing/providers/jaeger/pom.xml | 18 +++- .../providers/jaeger/JaegerTracerBuilder.java | 32 +++++-- .../jaeger/JaegerTracerBuilderTest.java | 30 ++++++- .../providers/jaeger/TestSpanExporter.java | 87 +++++++++++++++++++ .../jaeger/TestSpanExporterProvider.java | 48 ++++++++++ 5 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java create mode 100644 tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.java diff --git a/tracing/providers/jaeger/pom.xml b/tracing/providers/jaeger/pom.xml index 116887b6fd5..d02677b8a7e 100644 --- a/tracing/providers/jaeger/pom.xml +++ b/tracing/providers/jaeger/pom.xml @@ -154,12 +154,12 @@ org.apache.maven.plugins maven-surefire-plugin - + default-test - **/JaegerBaggagePropagationTest.java + **/JaegerBaggagePropagationTest.java,**/JaegerTracerBuilderTest.java @@ -176,6 +176,20 @@ **/JaegerBaggagePropagationTest.java + + tracer-builder-tests + + test + + + + + junit.jupiter.extensions.autodetection.enabled = true + + + **/JaegerTracerBuilderTest.java + + diff --git a/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java b/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java index 4f13506fd13..5bf59f6672f 100644 --- a/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java +++ b/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java @@ -18,6 +18,7 @@ import java.lang.System.Logger.Level; import java.time.Duration; +import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -37,6 +38,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; @@ -46,6 +48,7 @@ import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; @@ -179,6 +182,7 @@ public class JaegerTracerBuilder implements TracerBuilder { private int maxQueueSize = DEFAULT_MAX_QUEUE_SIZE; private int maxExportBatchSize = DEFAULT_MAX_EXPORT_BATCH_SIZE; private SpanProcessorType spanProcessorType = SpanProcessorType.BATCH; + private final List adHocExporters = new ArrayList<>(); // primarily for testing /** @@ -512,17 +516,25 @@ public Tracer build() { : Sampler.alwaysOff(); }; - Resource serviceName = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, this.serviceName)); + AttributesBuilder attributesBuilder = Attributes.builder() + .put(ResourceAttributes.SERVICE_NAME, serviceName); + tags.forEach(attributesBuilder::put); + Resource otelResource = Resource.create(attributesBuilder.build()); + + SdkTracerProviderBuilder sdkTracerProviderBuilder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(otelResource) + .addSpanProcessor(spanProcessor(exporter)); + adHocExporters.stream() + .map(this::spanProcessor) + .forEach(sdkTracerProviderBuilder::addSpanProcessor); + OpenTelemetry ot = OpenTelemetrySdk.builder() - .setTracerProvider(SdkTracerProvider.builder() - .addSpanProcessor(spanProcessor(exporter)) - .setSampler(sampler) - .setResource(serviceName) - .build()) + .setTracerProvider(sdkTracerProviderBuilder.build()) .setPropagators(ContextPropagators.create(TextMapPropagator.composite(createPropagators()))) .build(); - result = HelidonOpenTelemetry.create(ot, ot.getTracer(this.serviceName), tags); + result = HelidonOpenTelemetry.create(ot, ot.getTracer(this.serviceName), Map.of()); if (global) { GlobalOpenTelemetry.set(ot); @@ -604,6 +616,12 @@ List createPropagators() { .toList(); } + // Primarily for testing + JaegerTracerBuilder exporter(SpanExporter spanExporter) { + adHocExporters.add(spanExporter); + return this; + } + private SpanProcessor spanProcessor(SpanExporter exporter) { return switch (spanProcessorType) { case BATCH -> BatchSpanProcessor.builder(exporter) diff --git a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java index c2d14b93b0e..d97ddf28157 100644 --- a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java +++ b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.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. @@ -21,13 +21,18 @@ import java.util.Map; import io.helidon.config.Config; +import io.helidon.tracing.Span; import io.helidon.tracing.Tracer; import io.helidon.tracing.TracerBuilder; +import io.helidon.tracing.providers.opentelemetry.HelidonOpenTelemetry; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.extension.trace.propagation.JaegerPropagator; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.trace.data.SpanData; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -126,4 +131,27 @@ void testFullHttp() { assertThat(propagators.get(1), instanceOf(JaegerPropagator.class)); assertThat(propagators.get(2), instanceOf(W3CBaggagePropagator.class)); } + + @Test + void testProcessTagHandling() { + TracerBuilder builder = TracerBuilder.create(config.get("jaeger-defaults")); + builder.addTracerTag("tracerLevelTag", "val-1"); + JaegerTracerBuilder jaegerTracerBuilder = builder.unwrap(JaegerTracerBuilder.class); + jaegerTracerBuilder.scheduleDelay(Duration.ofMillis(100)); // for faster test runs + jaegerTracerBuilder.exporter(new TestSpanExporterProvider().createExporter(null)); + Tracer tracer = builder.build(); + + Span span = tracer.spanBuilder("testSpan").tag("spanLevelTag", "val-2").start(); + span.end(); + + try (TestSpanExporter exporter = TestSpanExporterProvider.exporter()) { + List spanData = exporter.spanData(1); + assertThat("Expect span count", spanData.size(), is(1)); + SpanData spanDataItem = spanData.get(0); + assertThat("Trace-level attribute tracerLevelTag", + spanDataItem.getResource().getAttributes().get(AttributeKey.stringKey("tracerLevelTag")), + is("val-1")); + } + + } } \ No newline at end of file diff --git a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java new file mode 100644 index 00000000000..bd64892307b --- /dev/null +++ b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java @@ -0,0 +1,87 @@ +/* + * 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.jaeger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import io.helidon.common.testing.junit5.MatcherWithRetry; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +import static org.hamcrest.Matchers.iterableWithSize; + +// Partially inspired by the MP Telemetry TCK InMemorySpanExporter. +public class TestSpanExporter implements SpanExporter { + + private final List spanData = new CopyOnWriteArrayList<>(); + private final System.Logger LOGGER = System.getLogger(TestSpanExporter.class.getName()); + + private final int RETRY_COUNT = Integer.getInteger(TestSpanExporter.class.getName() + ".test.retryCount", 120); + private final int RETRY_DELAY_MS = Integer.getInteger(TestSpanExporter.class.getName() + ".test.retryDelayMs", 500); + + + private enum State {READY, STOPPED} + + private State state = State.READY; + + @Override + public CompletableResultCode export(Collection collection) { + if (state == State.STOPPED) { + return CompletableResultCode.ofFailure(); + } + spanData.addAll(collection); + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + state = State.STOPPED; + spanData.clear(); + return CompletableResultCode.ofSuccess(); + } + + List spanData(int expectedCount) { + long startTime = 0; + if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) { + startTime = System.currentTimeMillis(); + } + var result = MatcherWithRetry.assertThatWithRetry("Expected span count", + () -> new ArrayList<>(spanData), + iterableWithSize(expectedCount), + RETRY_COUNT, + RETRY_DELAY_MS); + if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) { + LOGGER.log(System.Logger.Level.DEBUG, "spanData waited " + + (System.currentTimeMillis() - startTime) + + " ms for expected spans to arrive."); + } + return result; + } + + void clear() { + spanData.clear(); + } +} diff --git a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.java b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.java new file mode 100644 index 00000000000..022159d2800 --- /dev/null +++ b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.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.tracing.providers.jaeger; + +import io.helidon.common.LazyValue; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public class TestSpanExporterProvider implements ConfigurableSpanExporterProvider { + + private static final LazyValue SPAN_EXPORTER = LazyValue.create(TestSpanExporter::new); + + public TestSpanExporterProvider() { + System.err.println("provider ctor"); + } + + @Override + public SpanExporter createExporter(ConfigProperties configProperties) { + return SPAN_EXPORTER.get(); + } + + @Override + public String getName() { + return "in-memory"; + } + + static TestSpanExporter exporter() { + if (SPAN_EXPORTER.isLoaded()) { + return SPAN_EXPORTER.get(); + } + throw new IllegalStateException("Attempt to retrieve TestSpanExporter before it has been created"); + } +} From 774b5df42da8017dd391fc8c2ebd5cf64a9a6280 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Wed, 15 May 2024 14:19:38 -0500 Subject: [PATCH 006/245] Refactor a bit (#8745) --- .../oci/metrics/cdi/OciMetricsBean.java | 54 ++------- .../metrics/cdi/OciMetricsCdiExtension.java | 9 +- .../oci/metrics/OciMetricsSupport.java | 5 +- .../oci/metrics/OciMetricsSupportFactory.java | 105 ++++++++++++++++++ .../metrics/src/main/java/module-info.java | 4 +- 5 files changed, 126 insertions(+), 51 deletions(-) create mode 100644 integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupportFactory.java diff --git a/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsBean.java b/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsBean.java index 47682f73a75..e8bfc0d0678 100644 --- a/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsBean.java +++ b/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsBean.java @@ -17,6 +17,7 @@ import io.helidon.config.Config; import io.helidon.integrations.oci.metrics.OciMetricsSupport; +import io.helidon.integrations.oci.metrics.OciMetricsSupportFactory; import io.helidon.microprofile.server.RoutingBuilders; import com.oracle.bmc.monitoring.Monitoring; @@ -38,9 +39,8 @@ // This bean is added to handle injection on the ObserverMethod as it does not work on an Extension class. @Singleton -public class OciMetricsBean { +public class OciMetricsBean extends OciMetricsSupportFactory { - private Config ociMetricsConfig; private OciMetricsSupport ociMetricsSupport; /** @@ -50,63 +50,23 @@ public class OciMetricsBean { public OciMetricsBean() { } - /** - * Returns the config key to use for retrieving OCI metrics settings from the root config. - * - * @return config key for OCI metrics settings - */ + @Override protected String configKey() { return "ocimetrics"; } - /** - * 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) { - return OciMetricsSupport.builder() - .config(ociMetricsConfig) - .monitoringClient(monitoring); - } - - /** - * Returns the OCI metrics config settings previously retrieved from the config root. - * - * @return OCI metrics config node - */ - protected Config ociMetricsConfig() { - return ociMetricsConfig; - } - - /** - * Activates OCI metrics support. - * - * @param rootConfig root config node - * @param ociMetricsConfig OCI metrics configuration - * @param builder builder for {@link io.helidon.integrations.oci.metrics.OciMetricsSupport} - */ + @Override protected void activateOciMetricsSupport(Config rootConfig, Config ociMetricsConfig, OciMetricsSupport.Builder builder) { - ociMetricsSupport = builder.build(); + this.ociMetricsSupport = builder.build(); RoutingBuilders.create(ociMetricsConfig) .routingBuilder() .register(ociMetricsSupport); } + // Make Priority higher than MetricsCdiExtension so this will only start after MetricsCdiExtension has completed. void registerOciMetrics(@Observes @Priority(LIBRARY_BEFORE + 20) @Initialized(ApplicationScoped.class) Object ignore, Config rootConfig, Monitoring monitoringClient) { - ociMetricsConfig = rootConfig.get(configKey()); - OciMetricsSupport.Builder builder = ociMetricsSupportBuilder(rootConfig, ociMetricsConfig, monitoringClient); - if (builder.enabled()) { - activateOciMetricsSupport(rootConfig, ociMetricsConfig, builder); - } + registerOciMetrics(rootConfig, monitoringClient); } // For testing diff --git a/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsCdiExtension.java b/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsCdiExtension.java index 995c25d6da9..50498f270ca 100644 --- a/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsCdiExtension.java +++ b/integrations/oci/metrics/cdi/src/main/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsCdiExtension.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,13 @@ */ public class OciMetricsCdiExtension implements Extension { + /** + * For CDI use only. + */ + @Deprecated + public OciMetricsCdiExtension() { + } + // A new bean is added to handle the Observer Method as injection does not work here void addOciMetricsBean(@Observes BeforeBeanDiscovery event) { event.addAnnotatedType(OciMetricsBean.class, OciMetricsBean.class.getName()); diff --git a/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java b/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java index 7a15da26931..67c1e5db5ab 100644 --- a/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java +++ b/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.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. @@ -303,6 +303,9 @@ public static class Builder implements io.helidon.common.Builder + * This class is intended as an abstract superclass for CDI beans responsible for initializing an + * {@code OciMetricsSupport} instance using config and the {@link com.oracle.bmc.monitoring.Monitoring} instance. + * Concrete implementations implement must, of course, implement the abstract methods and might override other methods + * as needed. + *

+ * Callers typically invoke {@link #registerOciMetrics(io.helidon.config.Config, com.oracle.bmc.monitoring.Monitoring)} + * directly. + *

+ */ +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.mojo build-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.mock helidon-messaging-mock
+ + io.helidon.microprofile.testing + helidon-microprofile-testing-mocking + io.helidon.logging helidon-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.18 1.3.5 1.0.0 + 1.14.14 1.16.0 1.2 9.1.6 @@ -501,6 +502,11 @@ + + net.bytebuddy + byte-buddy + ${version.lib.bytebuddy} + jakarta.websocket jakarta.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-core test - 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> 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 @@ junit5 testng + 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.config helidon-config-yaml-mp - org.testng testng 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-core test + + 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-core test + + 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.config microprofile-config-api + + io.helidon.common + helidon-common-configurable + io.helidon.config helidon-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-configurable Helidon 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 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 entries; + private final Map computedKeys = new HashMap<>(); + private final Function> function; + + ComputedProperties(Function> function) { + this.function = function; + } + + Map computedKeys() { + return computedKeys; + } + + private Map entries() { + Map entries = this.entries; // volatile read + if (entries == null) { + Map map = new HashMap<>(); + computedKeys.forEach((key, computedKey) -> { + if (computedKey instanceof String s) { + map.put(key, function.apply(s).orElse(null)); + } + }); + entries = Collections.unmodifiableMap(map); + if (!ENTRIES.compareAndSet(this, null, entries)) { // volatile assignment, maybe + entries = this.entries; // volatile read; will only happen because this.entries will be non-null + } + } + return entries; + } + + @Override + public String getProperty(String key) { + if (entries().get(key) instanceof String s) { + return s; + } + return null; + } + + @Override + public int size() { + return computedKeys.size(); + } + + @Override + public boolean isEmpty() { + return computedKeys.isEmpty(); + } + + @Override + public Enumeration keys() { + return Collections.enumeration(computedKeys.keySet()); + } + + @Override + public Enumeration elements() { + return Collections.enumeration(entries().values()); + } + + @Override + public boolean contains(Object value) { + return containsValue(value); + } + + @Override + public boolean containsValue(Object value) { + return entries().containsValue(value); + } + + @Override + public boolean containsKey(Object key) { + return computedKeys.containsKey(key); + } + + @Override + public Object get(Object key) { + return entries().get(key); + } + + @Override + public synchronized String toString() { + return entries().toString(); + } + + @Override + public Set keySet() { + return computedKeys.keySet(); + } + + @Override + public Collection values() { + return entries().values(); + } + + @Override + public Set> entrySet() { + return entries().entrySet(); + } + + @Override + public synchronized boolean equals(Object o) { + return entries().equals(o); + } + + @Override + public synchronized int hashCode() { + return entries().hashCode(); + } + + @Override + public Object getOrDefault(Object key, Object defaultValue) { + return entries().getOrDefault(key, defaultValue); + } + + @Override + public synchronized void forEach(BiConsumer action) { + entries().forEach(action); + } + + @Override + public synchronized Object setProperty(String key, String value) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void load(Reader reader) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void load(InputStream inStream) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void loadFromXML(InputStream in) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object put(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void putAll(Map t) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void replaceAll(BiFunction function) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object putIfAbsent(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized boolean replace(Object key, Object oldValue, Object newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object replace(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object computeIfAbsent(Object key, Function mappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object computeIfPresent(Object key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object compute(Object key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object merge(Object key, + Object value, + BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object clone() { + throw new UnsupportedOperationException(); + } +} From 71bc198004dace2b0c77444c625a8b00f785689e Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 21 May 2024 15:33:50 -0400 Subject: [PATCH 012/245] New document that describes the ExecuteOn annotation (#8756) * New document that describes the ExecuteOn annotation. * Fixed import problems in snippets file. Signed-off-by: Santiago Pericas-Geertsen * Updates sitegen.yaml to include threading page. Signed-off-by: Santiago Pericas-Geertsen --------- Signed-off-by: Santiago Pericas-Geertsen --- docs/src/main/asciidoc/mp/threading.adoc | 138 ++++++++++++++++++ docs/src/main/asciidoc/sitegen.yaml | 6 + .../io/helidon/docs/mp/ExecuteOnSnippets.java | 72 +++++++++ 3 files changed, 216 insertions(+) create mode 100644 docs/src/main/asciidoc/mp/threading.adoc create mode 100644 docs/src/main/java/io/helidon/docs/mp/ExecuteOnSnippets.java diff --git a/docs/src/main/asciidoc/mp/threading.adoc b/docs/src/main/asciidoc/mp/threading.adoc new file mode 100644 index 00000000000..5e613936de5 --- /dev/null +++ b/docs/src/main/asciidoc/mp/threading.adoc @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + += Threading +:description: Threading in Helidon +:h1Prefix: MP +:pagename: threading +:feature-name: ExecuteOn +:keywords: helidon, webserver, theading, virtual, platform +:rootdir: {docdir}/.. + +include::{rootdir}/includes/mp.adoc[] + +== Contents +- <> +- <> +- <> +- <> +- <> + +== Overview + +Helidon 4 has been written from the ground up to take full advantage of Java 21’s virtual threads. With this +new architecture, threads are no longer a scarce resource that need to be pooled and managed, instead they +are an abundant resource that can be created as needed to satisfy your application needs. + +In most cases, users do not need to worry about thread management and are able to run any type of +task on virtual threads provided by the Helidon Webserver. +However, there are certain use cases where tasks may need to be executed on +platform threads: these include cpu-intensive tasks as well as tasks that may pin a virtual thread to +a platform thread due to the use of synchronized blocks. Many libraries that are typically used in Helidon +applications have been updated to take full advantage of virtual threads by avoiding unwanted synchronization blocks, +yet this process is still underway and some legacy libraries may never be fully converted. + +Helidon MP supports a new `@ExecuteOn` annotation to give developers full control on how to run +tasks. This annotation can be applied to any CDI bean method to control the type of thread in +which invocations of that method shall execute on. + +include::{rootdir}/includes/dependencies.adoc[] + +[source, xml] +---- + + io.helidon.microprofile + helidon-microprofile-cdi + +---- + +== API + +The API consists of a single `@ExecuteOn` annotation (with a few parameters) that can be applied to +any CDI bean method. + +NOTE: This feature is based on CDI interceptors, so using it on a non-CDI bean method will have no effect. + +[cols="3",role="flex, sm10"] +|=== +|Name |Value |Description +|value |ThreadType.PLATFORM, ThreadType.VIRTUAL, ThreadType.EXECUTOR |Type of thread used to execute a method invocation +|timeout |A long value |Maximum wait time for the method to return a value before triggering +a timeout exception +|unit |A `TimeUnit` value |Unit for `timeout` parameter +|executorName |The name of an executor from which to obtain a thread |CDI producer with a `@Named` +qualifier to access the executor +|=== + +== Configuration + +The implementation of the `@ExecuteOn` annotation takes advantage of Helidon's `ThreadPoolSupplier` +to (lazily) create a pool of platform threads. The default configuration for this thread +pool can be overridden using the (root) config key `execute-on.platform` as shown in the example +below. + +[source, yaml] +---- +execute-on: + platform: + thread-name-prefix: "my-platform-thread" + core-pool-size: 1 + max-pool-size: 2 + queue-capacity: 10 +---- + +For more information see the Javadoc for +link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/configurable/ThreadPoolSupplier.html[io.helidon.common.configurable.ThreadPoolSupplier]. +For virtual threads, only the thread name prefix can be overridden as follows: + +[source, yaml] +---- +execute-on: + virtual: + thread-name-prefix: "my-virtual-thread" +---- + +== Examples + +1. The following example creates a new platform thread from a (configurable) default pool +to execute a cpu-intensive task. Platform threads are a scarce resource, creating threads of +type `PLATFORM` should be done responsibly! ++ +[source,java] +---- +include::{sourcedir}/mp/ExecuteOnSnippets.java[tag=snippet_1, indent=0] +---- + +2. The next example also uses a platform thread, but this time the developer is also +responsible for providing an executor; this is done by creating a CDI provider with the same +executor name (using the `@Named` annotation) as the one in the annotation parameter. +Note that for simplicity the producer in this example is also part of the same bean, +but that is not a requirement in CDI. ++ +[source,java] +---- +include::{sourcedir}/mp/ExecuteOnSnippets.java[tag=snippet_2, indent=0] +---- + +3. Finally, it is also possible to explicitly execute a method in a +virtual thread, blocking the caller thread until the method execution is complete. ++ +[source,java] +---- +include::{sourcedir}/mp/ExecuteOnSnippets.java[tag=snippet_3, indent=0] +---- diff --git a/docs/src/main/asciidoc/sitegen.yaml b/docs/src/main/asciidoc/sitegen.yaml index ecebf570d05..ffceb56b2ec 100644 --- a/docs/src/main/asciidoc/sitegen.yaml +++ b/docs/src/main/asciidoc/sitegen.yaml @@ -245,6 +245,12 @@ backend: glyph: type: "icon" value: "access_alarm" + - type: "PAGE" + title: "Threading" + source: "threading.adoc" + glyph: + type: "icon" + value: "clear_all" - type: "MENU" title: "Security" dir: "security" diff --git a/docs/src/main/java/io/helidon/docs/mp/ExecuteOnSnippets.java b/docs/src/main/java/io/helidon/docs/mp/ExecuteOnSnippets.java new file mode 100644 index 00000000000..d3258a00d17 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/ExecuteOnSnippets.java @@ -0,0 +1,72 @@ +/* + * 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.docs.mp; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import io.helidon.microprofile.cdi.ExecuteOn; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Named; + +import static io.helidon.microprofile.cdi.ExecuteOn.ThreadType; + +@SuppressWarnings("ALL") +class ExecuteOnSnippets { + + // stub + static class PiDigitCalculator { + static int nthDigitOfPi(int n) { + return 0; + } + } + + // tag::snippet_1[] + public class MyPlaformBean { + + @ExecuteOn(ThreadType.PLATFORM) + int cpuIntensive(int n) { + return PiDigitCalculator.nthDigitOfPi(n); + } + } + // end::snippet_1[] + + // tag::snippet_2[] + public class MyExecutorBean { + + @ExecuteOn(value = ThreadType.EXECUTOR, executorName = "my-executor") + int cpuIntensive(int n) { + return PiDigitCalculator.nthDigitOfPi(n); + } + + @Produces + @Named("my-executor") + ExecutorService myExecutor() { + return Executors.newFixedThreadPool(2); + } + } + // end::snippet_2[] + + // tag::snippet_3[] + public class MyVirtualBean { + + @ExecuteOn(ThreadType.VIRTUAL) + void someTask() { + // run task on virtual thread + } + } + // end::snippet_3[] +} From e0a91cb10c1488516e8b14513a4e7a79402d6232 Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Tue, 21 May 2024 16:10:08 -0700 Subject: [PATCH 013/245] 4.x: cleanup helidon-bom and helidon-all (#8783) * Remove artifacts we do not deploy from helidon-bom --- all/pom.xml | 8 -------- bom/pom.xml | 16 ---------------- inject/tests/resources-inject/pom.xml | 1 + inject/tests/tck-jsr330/pom.xml | 1 + metrics/providers/pom.xml | 1 + tracing/providers/pom.xml | 1 + 6 files changed, 4 insertions(+), 24 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 10792daf584..6ea7065eb75 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -293,10 +293,6 @@ io.helidon.metrics helidon-metrics-system-meters - - io.helidon.metrics - helidon-metrics-provider-tests - io.helidon.metrics.providers helidon-metrics-providers-micrometer @@ -1034,10 +1030,6 @@ io.helidon.inject helidon-inject-processor - - io.helidon.inject - helidon-inject-maven-plugin - io.helidon.inject helidon-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.providers helidon-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.inject helidon-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.inject helidon-inject-maven-plugin + ${helidon.version} provided true 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.inject helidon-inject-maven-plugin + ${helidon.version} provided true 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.metrics helidon-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.tracing helidon-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.1 1.6 3.1.1 1.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-hikaricp runtime + + org.slf4j + slf4j-jdk14 + 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.db ojdbc runtime + + 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.6 4.0.6 3.3.0 - 0.9.27 + 0.10.2 1.5.0.Final 0.6.1 3.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.6 3.1.2 3.0.2 + 0.10.2 1.5.0.Final 0.5.1 2.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.6 4.0.6 3.0.2 + 0.10.2 1.5.0.Final 0.5.1 2.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.quickstarts helidon-quickstart-mp Helidon 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.quickstarts helidon-quickstart-se 4.0.0-SNAPSHOT Helidon 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-image helidon-tests-native-image-mp-1 Helidon 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-image helidon-tests-native-image-mp-2 Helidon 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-image helidon-tests-native-image-mp-3 Helidon 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-image helidon-tests-native-image-se-1 Helidon 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.micrometer helidon-integrations-micrometer-cdi + + io.helidon.integrations.oci + helidon-integrations-oci + io.helidon.integrations.oci.sdk helidon-integrations-oci-sdk-cdi @@ -1050,6 +1054,14 @@ io.helidon.inject.configdriven helidon-inject-configdriven-processor + + io.helidon.service + helidon-service-registry + + + io.helidon.service + helidon-service-codegen + io.helidon.integrations.oci.sdk helidon-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.sdk helidon-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.builder helidon-builder-api + + io.helidon.service + helidon-service-registry + com.fasterxml.jackson.core jackson-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-types builder + 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 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.common helidon-common @@ -61,6 +50,10 @@ io.helidon.builder helidon-builder-api + + + io.helidon.service + helidon-service-registry true 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 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 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 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 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 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 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 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 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 - io.helidon.inject - helidon-inject-api + io.helidon.builder + helidon-builder-api - io.helidon.inject - helidon-inject-runtime + io.helidon.service + helidon-service-registry true @@ -107,54 +107,58 @@ org.apache.maven.plugins maven-compiler-plugin - - -Ainject.acceptPreview=true - io.helidon.common.features helidon-common-features-processor ${helidon.version} + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + io.helidon.builder - helidon-builder-processor + helidon-builder-codegen ${helidon.version} - io.helidon.inject - helidon-inject-processor + 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} - io.helidon.common.features helidon-common-features-processor ${helidon.version} - - io.helidon.inject - helidon-inject-processor + 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.common.processor - helidon-common-processor-helidon-copyright + io.helidon.codegen + helidon-codegen-helidon-copyright ${helidon.version} diff --git a/config/config/src/main/java/io/helidon/config/Config.java b/config/config/src/main/java/io/helidon/config/Config.java index 62f015c0a97..f34c4a88d38 100644 --- a/config/config/src/main/java/io/helidon/config/Config.java +++ b/config/config/src/main/java/io/helidon/config/Config.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. @@ -38,6 +38,7 @@ import io.helidon.config.spi.ConfigSource; import io.helidon.config.spi.MergingStrategy; import io.helidon.config.spi.OverrideSource; +import io.helidon.service.registry.Service; /** *

Configuration

@@ -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. - *

*

Handling Multiple Configuration * Sources

* 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-registry io.helidon.config helidon-config-yaml test - - io.helidon.inject - helidon-inject-testing - test - org.junit.jupiter junit-jupiter-api @@ -70,31 +61,38 @@ org.apache.maven.plugins maven-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 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 @@ graphql health integrations - inject jbatch logging media 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.common helidon-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.hamcrest hamcrest-all @@ -129,11 +95,6 @@ helidon-config-metadata-processor ${helidon.version} - - io.helidon.inject.configdriven - helidon-inject-configdriven-processor - ${helidon.version} - io.helidon.codegen helidon-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.features helidon-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.features helidon-common-features + + io.helidon.service + helidon-service-registry + org.eclipse.parsson parsson 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 config-file}: Config file based authentication details provider (default weight - 20)
    • + *
    • {@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 + oci metrics - oci-secrets-config-source - oci-secrets-mp-config-source + secrets-config-source + secrets-mp-config-source sdk tls-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.sdk helidon-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.testing helidon-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.sdk helidon-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.testing helidon-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-oci com.oracle.oci.sdk @@ -92,7 +96,6 @@ - io.helidon.config helidon-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 secretsSupplier; @@ -90,36 +87,39 @@ protected Builder() { this.secretsSupplier = () -> scb.build(adpSupplier().get()); } + static LazyValue 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 secretsSupplier) { @@ -159,12 +151,12 @@ public B secretsSupplier(Supplier secretsSupplier) { return (B) this; } - Supplier secretsSupplier() { - return this.secretsSupplier; + String vaultOcid() { + return this.vaultOcid; } - static LazyValue adpSupplier() { - return LazyValue.create(() -> (BasicAuthenticationDetailsProvider) ociAuthenticationProvider().get()); + Supplier 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> 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 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> loader;
    -
    -    private final Supplier 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 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 vaultsSupplier,
    -                                       Supplier secretsSupplier,
    -                                       ListSecretsRequest listSecretsRequest) {
    -        Collection secretSummaries = secretSummaries(vaultsSupplier, listSecretsRequest);
    -        return this.load(secretSummaries, secretsSupplier);
    -    }
    -
    -    private Optional load(Collection secretSummaries,
    -                                       Supplier 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 secretSummaries,
    -                                 Supplier 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 secretSummaries, Set eTags) {
    +    static Stamp toStamp(Collection secretSummaries, Set eTags) {
             if (secretSummaries.isEmpty()) {
                 return new Stamp();
             }
    @@ -255,34 +145,79 @@ static Stamp toStamp(Collection secretSummaries, Set 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 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> 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> tasks, Au
             }
         }
     
    -    private static void completeTasks(ExecutorService es, Collection> 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> 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 secretSummaries(Supplier vaultsSupplier,
    -                                                                       ListSecretsRequest listSecretsRequest) {
    +    private static Collection secretSummaries(Supplier vaultsSupplier,
    +                                                             ListSecretsRequest listSecretsRequest) {
             try (Vaults v = vaultsSupplier.get()) {
                 return v.listSecrets(listSecretsRequest).getItems();
             } catch (RuntimeException e) {
    @@ -363,31 +292,16 @@ private static Collection secretSummaries(Supplier 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
    -        };
    -    }
    -
         private static Base64SecretBundleContentDetails
    -        secretBundleContentDetails(GetSecretBundleRequest request,
    -                                   Function f,
    -                                   Consumer 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 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 vaultsSupplier,
    +                                       Supplier secretsSupplier,
    +                                       ListSecretsRequest listSecretsRequest) {
    +        Collection secretSummaries = secretSummaries(vaultsSupplier, listSecretsRequest);
    +        return this.load(secretSummaries, secretsSupplier);
    +    }
     
    -    /*
    -     * Inner and nested classes.
    -     */
    -
    +    private Optional load(Collection secretSummaries,
    +                                       Supplier 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 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 vaultsSupplier) { this.vaultsSupplier = Objects.requireNonNull(vaultsSupplier, "vaultsSupplier"); @@ -532,10 +448,9 @@ public Builder vaultsSupplier(Supplier 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.hamcrest hamcrest-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.sdk oci-java-sdk-vault - - jakarta.inject - jakarta.inject-api - true - - - jakarta.annotation - jakarta.annotation-api - true - - - io.helidon.config - helidon-config-metadata - true - io.helidon.builder helidon-builder-api + + io.helidon.service + helidon-service-registry + io.helidon.common helidon-common-tls @@ -82,25 +71,17 @@ io.helidon.common helidon-common-key-util - - io.helidon.integrations.oci.sdk - helidon-integrations-oci-sdk-runtime - io.helidon.fault-tolerance helidon-fault-tolerance - - io.helidon.config - helidon-config-yaml - io.helidon.scheduling helidon-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-jersey3 test - io.helidon.common.testing helidon-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-yaml test - @@ -148,82 +117,59 @@ org.apache.maven.plugins maven-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 @@ webserver websocket codegen + 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> SUPPORTED_OPTIONS = Set.of( + ServiceOptions.AUTO_ADD_NON_CONTRACT_INTERFACES + ); + + private static final Set SUPPORTED_ANNOTATIONS = Set.of( + TypeNames.GENERATED, + ServiceCodegenTypes.SERVICE_ANNOTATION_DESCRIPTOR, + ServiceCodegenTypes.SERVICE_ANNOTATION_PROVIDER + ); + private static final Set SUPPORTED_ANNOTATION_PACKAGES = Set.of(); + + /** + * Required default constructor. + * + * @deprecated required by {@link java.util.ServiceLoader} + */ + @Deprecated + public ServiceRegistryCodegenProvider() { + } + + @Override + public Set> supportedOptions() { + return SUPPORTED_OPTIONS; + } + + @Override + public Set supportedAnnotations() { + return SUPPORTED_ANNOTATIONS; + } + + @Override + public Set supportedAnnotationPackages() { + return SUPPORTED_ANNOTATION_PACKAGES; + } + + @Override + public CodegenExtension create(CodegenContext ctx, TypeName generatorType) { + return ServiceRegistryCodegenExtension.create(ctx, generatorType); + } +} diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/SuperType.java b/service/codegen/src/main/java/io/helidon/service/codegen/SuperType.java new file mode 100644 index 00000000000..ac189297b54 --- /dev/null +++ b/service/codegen/src/main/java/io/helidon/service/codegen/SuperType.java @@ -0,0 +1,36 @@ +/* + * 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 io.helidon.common.types.TypeInfo; +import io.helidon.common.types.TypeName; + +/** + * Definition of a super type (if any). + * + * @param hasSupertype whether there is a super type (declared through {@code extends SuperType}) + * @param superDescriptorType type name of the service descriptor of the super type + * @param superType type info of the super type + */ +record SuperType(boolean hasSupertype, + TypeName superDescriptorType, + TypeInfo superType, + boolean superTypeIsCore) { + static SuperType noSuperType() { + return new SuperType(false, null, null, false); + } +} diff --git a/service/codegen/src/main/java/io/helidon/service/codegen/TypedElements.java b/service/codegen/src/main/java/io/helidon/service/codegen/TypedElements.java new file mode 100644 index 00000000000..f05eebab293 --- /dev/null +++ b/service/codegen/src/main/java/io/helidon/service/codegen/TypedElements.java @@ -0,0 +1,95 @@ +/* + * 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.List; + +import io.helidon.codegen.ElementInfoPredicates; +import io.helidon.common.types.AccessModifier; +import io.helidon.common.types.ElementKind; +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 java.util.function.Predicate.not; + +final class TypedElements { + static final ElementMeta DEFAULT_CONSTRUCTOR = new ElementMeta(TypedElementInfo.builder() + .typeName(TypeNames.OBJECT) + .accessModifier(AccessModifier.PUBLIC) + .kind(ElementKind.CONSTRUCTOR) + .build()); + + private TypedElements() { + } + + static List gatherElements(TypeInfo typeInfo) { + List result = new ArrayList<>(); + + List declaredElements = typeInfo.elementInfo() + .stream() + .toList(); + + for (TypedElementInfo declaredMethod : declaredElements) { + List interfaceMethods = new ArrayList<>(); + + if (declaredMethod.kind() == ElementKind.METHOD) { + // now find the same method on any interface (if declared there) + for (TypeInfo info : typeInfo.interfaceTypeInfo()) { + info.elementInfo() + .stream() + .filter(ElementInfoPredicates::isMethod) + .filter(not(ElementInfoPredicates::isStatic)) + .filter(not(ElementInfoPredicates::isPrivate)) + .filter(it -> signatureMatches(declaredMethod, it)) + .findFirst() + .ifPresent(it -> interfaceMethods.add(new TypedElements.DeclaredElement(info, it))); + } + } + result.add(new TypedElements.ElementMeta(declaredMethod, interfaceMethods)); + } + + return result; + } + + private static boolean signatureMatches(TypedElementInfo method, TypedElementInfo interfaceMethod) { + // if the method has the same name and same parameter types, it is our candidate (return type MUST be the same, + // as otherwise this could not be compiled + if (!ElementInfoPredicates.elementName(method.elementName()).test(interfaceMethod)) { + return false; + } + List expectedParams = method.parameterArguments() + .stream() + .map(TypedElementInfo::typeName) + .toList(); + + return ElementInfoPredicates.hasParams(expectedParams).test(interfaceMethod); + } + + record ElementMeta(TypedElementInfo element, + List interfaceMethods) { + ElementMeta(TypedElementInfo element) { + this(element, List.of()); + } + } + + record DeclaredElement(TypeInfo iface, + TypedElementInfo element) { + } +} diff --git a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/package-info.java b/service/codegen/src/main/java/io/helidon/service/codegen/package-info.java similarity index 80% rename from examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/package-info.java rename to service/codegen/src/main/java/io/helidon/service/codegen/package-info.java index 54f3306b2c0..7a1f66cfe26 100644 --- a/examples/inject/configdriven/src/main/java/io/helidon/examples/inject/configdriven/package-info.java +++ b/service/codegen/src/main/java/io/helidon/service/codegen/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. @@ -15,6 +15,6 @@ */ /** - * Examples of Config-Driven Services. + * Code generation for Helidon Service Registry. */ -package io.helidon.examples.inject.configdriven; +package io.helidon.service.codegen; diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Blade.java b/service/codegen/src/main/java/module-info.java similarity index 53% rename from examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Blade.java rename to service/codegen/src/main/java/module-info.java index 19ec44abb15..58bbe69d2ad 100644 --- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/Blade.java +++ b/service/codegen/src/main/java/module-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. @@ -14,19 +14,18 @@ * limitations under the License. */ -package io.helidon.examples.inject.providers; - -import io.helidon.inject.api.Contract; -import io.helidon.inject.api.Services; +import io.helidon.service.codegen.ServiceRegistryCodegenProvider; /** - * 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}. + * Code generation for Helidon Service Registry. */ -//@Contract -public interface Blade { +module io.helidon.service.codegen { + requires transitive io.helidon.builder.api; + requires transitive io.helidon.codegen.classmodel; + requires transitive io.helidon.codegen; - String name(); + exports io.helidon.service.codegen; -} + provides io.helidon.codegen.spi.CodegenExtensionProvider + with ServiceRegistryCodegenProvider; +} \ No newline at end of file diff --git a/service/pom.xml b/service/pom.xml new file mode 100644 index 00000000000..412833b6902 --- /dev/null +++ b/service/pom.xml @@ -0,0 +1,63 @@ + + + + + 4.0.0 + + io.helidon + helidon-project + 4.0.0-SNAPSHOT + + + io.helidon.service + helidon-service-project + Helidon Service Project + + Service declaration and registry support. + + + pom + + + true + + + + codegen + registry + tests + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + check-dependencies + verify + + + + + + diff --git a/service/registry/etc/spotbugs/exclude.xml b/service/registry/etc/spotbugs/exclude.xml new file mode 100644 index 00000000000..bafc73c84e1 --- /dev/null +++ b/service/registry/etc/spotbugs/exclude.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/service/registry/pom.xml b/service/registry/pom.xml new file mode 100644 index 00000000000..0e9379c3835 --- /dev/null +++ b/service/registry/pom.xml @@ -0,0 +1,105 @@ + + + + + + io.helidon.service + helidon-service-project + 4.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + helidon-service-registry + Helidon Service Registry + + Service registry (a replacement for service loader that allows dependencies on other services), + and API necessary to declare services. + + + + etc/spotbugs/exclude.xml + + + + + io.helidon.common + helidon-common + + + io.helidon.common + helidon-common-types + + + io.helidon.builder + helidon-builder-api + + + io.helidon.common + helidon-common-config + + + + + + + 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.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.builder + helidon-builder-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + diff --git a/service/registry/src/main/java/io/helidon/service/registry/CoreServiceDiscovery.java b/service/registry/src/main/java/io/helidon/service/registry/CoreServiceDiscovery.java new file mode 100644 index 00000000000..3219668d9d3 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/CoreServiceDiscovery.java @@ -0,0 +1,250 @@ +/* + * 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.registry; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.System.Logger.Level; +import java.lang.reflect.Field; +import java.net.URL; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import io.helidon.common.LazyValue; +import io.helidon.common.Weighted; +import io.helidon.common.Weights; +import io.helidon.common.types.TypeName; +import io.helidon.service.registry.GeneratedService.Descriptor; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.function.Predicate.not; + +class CoreServiceDiscovery implements ServiceDiscovery { + private static final System.Logger LOGGER = System.getLogger(CoreServiceDiscovery.class.getName()); + + private final List allDescriptors; + + private CoreServiceDiscovery(ServiceRegistryConfig config) { + Map allDescriptors = new LinkedHashMap<>(); + + ClassLoader classLoader = classLoader(); + + // each line is a type:service-descriptor:weight:contract,contract + if (config.discoverServices()) { + classLoader.resources(SERVICES_RESOURCE) + .flatMap(CoreServiceDiscovery::loadLines) + .filter(not(Line::isEmpty)) + .filter(not(Line::isComment)) + .flatMap(DescriptorMeta::parse) + .forEach(it -> allDescriptors.putIfAbsent(it.descriptorType(), it)); + } + + List result = new ArrayList<>(allDescriptors.values()); + + if (config.discoverServicesFromServiceLoader()) { + // each line is a provider type name (and may have zero or more implementations) + classLoader.resources(SERVICES_LOADER_RESOURCE) + .flatMap(CoreServiceDiscovery::loadLines) + .filter(not(Line::isEmpty)) + .filter(not(Line::isComment)) + .flatMap(DescriptorMeta::parseServiceProvider) + .forEach(result::add); + } + + this.allDescriptors = List.copyOf(result); + } + + static ServiceDiscovery create(ServiceRegistryConfig config) { + return new CoreServiceDiscovery(config); + } + + static ServiceDiscovery noop() { + return NoopServiceDiscovery.INSTANCE; + } + + @Override + public List allMetadata() { + return allDescriptors; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static Class toClass(TypeName className) { + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + cl = (cl == null) ? CoreServiceDiscovery.class.getClassLoader() : cl; + + return (Class) cl.loadClass(className.fqName()); + } catch (ClassNotFoundException e) { + throw new ServiceRegistryException("Resolution of type \"" + className.fqName() + + "\" to class failed.", + e); + } + } + + private static Descriptor getDescriptorInstance(TypeName descriptorType) { + Class clazz = toClass(descriptorType); + + try { + Field field = clazz.getField("INSTANCE"); + return (Descriptor) field.get(null); + } catch (ReflectiveOperationException e) { + throw new ServiceRegistryException("Could not obtain the instance of service descriptor " + + descriptorType.fqName(), + e); + } + } + + private static ClassLoader classLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + return CoreServiceDiscovery.class.getClassLoader(); + } + return cl; + } + + private static Stream loadLines(URL url) { + + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { + List lines = new ArrayList<>(); + + String line; + int lineNumber = 0; + + while ((line = br.readLine()) != null) { + lineNumber++; // we want to start with 1 + lines.add(new Line(url.toString(), line, lineNumber)); + } + + return lines.stream(); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Failed to read services from " + url, e); + return Stream.of(); + } + } + + private static DescriptorMeta createServiceProviderDescriptor(TypeName providerType, + ServiceLoader.Provider provider) { + Class serviceClass = provider.type(); + double weight = Weights.find(serviceClass, Weighted.DEFAULT_WEIGHT); + + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, + "Discovered service provider for type %s: %s, weight: %s".formatted(providerType.fqName(), + serviceClass.getName(), + weight)); + } + + Descriptor descriptor = ServiceLoader__ServiceDescriptor.create(providerType, provider, weight); + return new DescriptorMeta("core", + descriptor.descriptorType(), + weight, + descriptor.contracts(), + LazyValue.create(descriptor)); + } + + private record Line(String source, String line, int lineNumber) { + boolean isEmpty() { + return line.isEmpty(); + } + + boolean isComment() { + return line.startsWith("#"); + } + } + + private record DescriptorMeta(String registryType, + TypeName descriptorType, + double weight, + Set contracts, + LazyValue> descriptorSupplier) implements DescriptorMetadata { + DescriptorMeta(String registryType, TypeName descriptorType, double weight, Set contract) { + this(registryType, descriptorType, weight, contract, LazyValue.create(() -> getDescriptorInstance(descriptorType))); + } + + @Override + public Descriptor descriptor() { + return descriptorSupplier.get(); + } + + private static Stream parseServiceProvider(Line line) { + // io.helidon.config.ConfigSource + TypeName providerType = TypeName.create(line.line.trim()); + + Class providerClass = toClass(providerType); + CoreServiceDiscovery.class.getModule() + .addUses(providerClass); + + ServiceLoader serviceLoader = ServiceLoader.load(providerClass); + + return serviceLoader.stream() + .map(it -> CoreServiceDiscovery.createServiceProviderDescriptor(providerType, it)); + } + + private static Stream parse(Line line) { + // core:io.helidon.ContractImpl__ServiceDescriptor:101.3:io.helidon.Contract,io.helidon.Contract2 + // inject:io.helidon.ContractImpl__ServiceDescriptor:101.3:io.helidon.Contract,io.helidon.Contract2 + String[] components = line.line().split(":"); + if (components.length < 4) { + // allow more, if we need more info in the future, to be backward compatible for libraries + LOGGER.log(Level.WARNING, + "Line " + line.lineNumber() + " of " + line.source() + + " is invalid, should be registry-type:service-descriptor:weight:contracts"); + } + try { + String registryType = components[0]; + TypeName descriptor = TypeName.create(components[1]); + double weight = Double.parseDouble(components[2]); + + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, + "Discovered service descriptor %s, weight: %s".formatted(descriptor.fqName(), + weight)); + } + + Set contracts = Stream.of(components[3].split(",")) + .map(String::trim) + .map(TypeName::create) + .collect(Collectors.toSet()); + + return Stream.of(new DescriptorMeta(registryType, descriptor, weight, contracts)); + } catch (RuntimeException e) { + LOGGER.log(Level.WARNING, + "Line " + line.lineNumber() + " of " + line.source() + + " is invalid, should be service-descriptor:weight:contracts", + e); + return Stream.empty(); + } + } + } + + static class NoopServiceDiscovery implements ServiceDiscovery { + private static final ServiceDiscovery INSTANCE = new NoopServiceDiscovery(); + + @Override + public List allMetadata() { + return List.of(); + } + } +} + diff --git a/service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistry.java b/service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistry.java new file mode 100644 index 00000000000..b6c2ab98123 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistry.java @@ -0,0 +1,332 @@ +/* + * 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.registry; + +import java.lang.System.Logger.Level; +import java.util.ArrayList; +import java.util.Comparator; +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.TreeSet; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import io.helidon.common.LazyValue; +import io.helidon.common.types.TypeName; +import io.helidon.service.registry.GeneratedService.Descriptor; + +/** + * Basic implementation of the service registry with simple dependency support. + */ +class CoreServiceRegistry implements ServiceRegistry { + private static final System.Logger LOGGER = System.getLogger(CoreServiceRegistry.class.getName()); + + private static final Comparator PROVIDER_COMPARATOR = + Comparator.comparing(ServiceProvider::weight).reversed() + .thenComparing(ServiceProvider::descriptorType); + + private final Map> providersByContract; + private final Map providersByService; + + CoreServiceRegistry(ServiceRegistryConfig config, ServiceDiscovery serviceDiscovery) { + Map> providers = new HashMap<>(); + Map providersByService = new IdentityHashMap<>(); + + // each just once + Set processedDescriptorTypes = new HashSet<>(); + + // add me + ServiceRegistry__ServiceDescriptor registryDescriptor = ServiceRegistry__ServiceDescriptor.INSTANCE; + processedDescriptorTypes.add(registryDescriptor.descriptorType()); + addContracts(providers, registryDescriptor.contracts(), new BoundInstance(registryDescriptor, Optional.of(this))); + + // add explicit instances + config.serviceInstances().forEach((descriptor, instance) -> { + if (processedDescriptorTypes.add(descriptor.descriptorType())) { + BoundInstance bi = new BoundInstance(descriptor, Optional.of(instance)); + providersByService.put(descriptor, bi); + addContracts(providers, descriptor.contracts(), bi); + } + }); + + // add configured descriptors + for (Descriptor descriptor : config.serviceDescriptors()) { + if (processedDescriptorTypes.add(descriptor.descriptorType())) { + BoundDescriptor bd = new BoundDescriptor(this, descriptor, LazyValue.create(() -> instance(descriptor))); + providersByService.put(descriptor, bd); + addContracts(providers, descriptor.contracts(), bd); + } + } + + boolean logUnsupported = LOGGER.isLoggable(Level.TRACE); + + // and finally add discovered instances + for (DescriptorMetadata descriptorMeta : serviceDiscovery.allMetadata()) { + if (!descriptorMeta.registryType().equals(DescriptorMetadata.REGISTRY_TYPE_CORE)) { + // we can only support core services, others should be handled by other registry implementations + if (logUnsupported) { + LOGGER.log(Level.TRACE, + "Ignoring service of type \"" + descriptorMeta.registryType() + "\": " + descriptorMeta); + } + continue; + } + if (processedDescriptorTypes.add(descriptorMeta.descriptor().serviceType())) { + DiscoveredDescriptor dd = new DiscoveredDescriptor(this, + descriptorMeta, + LazyValue.create(() -> instance(descriptorMeta.descriptor()))); + providersByService.put(descriptorMeta.descriptor(), dd); + addContracts(providers, descriptorMeta.contracts(), dd); + } + } + this.providersByContract = Map.copyOf(providers); + this.providersByService = providersByService; + } + + @Override + public T get(TypeName contract) { + return this.first(contract) + .orElseThrow(() -> new ServiceRegistryException("Contract " + contract.fqName() + + " is not supported, there are no service " + + "descriptors in this registry that can satisfy it.")); + } + + @SuppressWarnings("unchecked") + @Override + public Optional first(TypeName contract) { + return allProviders(contract) + .stream() + .flatMap(it -> it.instance().stream()) + .findFirst() + .map(it -> (T) it); + } + + @SuppressWarnings("unchecked") + @Override + public List all(TypeName contract) { + return allProviders(contract) + .stream() + .flatMap(it -> it.instance().stream()) + .map(it -> (T) it) + .collect(Collectors.toList()); + } + + @Override + public Supplier supply(TypeName contract) { + return LazyValue.create(() -> get(contract)); + } + + @Override + public Supplier> supplyFirst(TypeName contract) { + return LazyValue.create(() -> first(contract)); + } + + @Override + public Supplier> supplyAll(TypeName contract) { + return LazyValue.create(() -> all(contract)); + } + + @SuppressWarnings("unchecked") + @Override + public Optional get(ServiceInfo serviceInfo) { + return Optional.ofNullable(providersByService.get(serviceInfo)) + .flatMap(ServiceProvider::instance) + .map(it -> (T) it); + } + + @Override + public List allServices(TypeName contract) { + return Optional.ofNullable(providersByContract.get(contract)) + .orElseGet(Set::of) + .stream() + .map(ServiceProvider::descriptor) + .collect(Collectors.toUnmodifiableList()); + + } + + private static void addContracts(Map> providers, + Set contracts, + ServiceProvider provider) { + for (TypeName contract : contracts) { + providers.computeIfAbsent(contract, it -> new TreeSet<>(PROVIDER_COMPARATOR)) + .add(provider); + } + } + + private List allProviders(TypeName contract) { + Set serviceProviders = providersByContract.get(contract); + if (serviceProviders == null) { + return List.of(); + } + + return new ArrayList<>(serviceProviders); + } + + private Optional instance(Descriptor descriptor) { + List dependencies = descriptor.dependencies(); + Map collectedDependencies = new HashMap<>(); + + for (Dependency dependency : dependencies) { + TypeName contract = dependency.contract(); + TypeName dependencyType = dependency.typeName(); + + if (dependencyType.isSupplier()) { + TypeName first = dependencyType.typeArguments().getFirst(); + collectedDependencies.put(dependency, (Supplier) () -> dependencyNoSupplier(first, contract)); + } else { + collectedDependencies.put(dependency, dependencyNoSupplier(dependencyType, contract)); + } + } + + Object serviceInstance = descriptor.instantiate(DependencyContext.create(collectedDependencies)); + if (serviceInstance instanceof Supplier supplier) { + return fromSupplierValue(supplier.get()); + } + return Optional.of(serviceInstance); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private Optional fromSupplierValue(Object value) { + if (value instanceof Optional optValue) { + return optValue; + } + return Optional.of(value); + } + + private Object dependencyNoSupplier(TypeName dependencyType, TypeName contract) { + if (dependencyType.isList()) { + return all(contract); + } else if (dependencyType.isOptional()) { + return first(contract); + } else { + // contract dependency + return get(contract); + } + } + + private interface ServiceProvider { + Descriptor descriptor(); + + Optional instance(); + + double weight(); + + TypeName descriptorType(); + } + + private record BoundInstance(Descriptor descriptor, Optional instance) implements ServiceProvider { + @Override + public double weight() { + return descriptor.weight(); + } + + @Override + public TypeName descriptorType() { + return descriptor.descriptorType(); + } + } + + private record BoundDescriptor(CoreServiceRegistry registry, + Descriptor descriptor, + LazyValue> lazyInstance, + ReentrantLock lock) implements ServiceProvider { + + private BoundDescriptor(CoreServiceRegistry registry, + Descriptor descriptor, + LazyValue> lazyInstance) { + this(registry, descriptor, lazyInstance, new ReentrantLock()); + } + + @Override + public Optional instance() { + if (lazyInstance.isLoaded()) { + return lazyInstance.get(); + } + + if (lock.isHeldByCurrentThread()) { + throw new ServiceRegistryException("Cyclic dependency, attempting to obtain an instance of " + + descriptor.serviceType().fqName() + " while instantiating it"); + } + try { + lock.lock(); + return lazyInstance.get(); + } finally { + lock.unlock(); + } + } + + @Override + public double weight() { + return descriptor.weight(); + } + + @Override + public TypeName descriptorType() { + return descriptor.descriptorType(); + } + } + + private record DiscoveredDescriptor(CoreServiceRegistry registry, + DescriptorMetadata metadata, + LazyValue> lazyInstance, + ReentrantLock lock) implements ServiceProvider { + + private DiscoveredDescriptor(CoreServiceRegistry registry, + DescriptorMetadata metadata, + LazyValue> lazyInstance) { + this(registry, metadata, lazyInstance, new ReentrantLock()); + } + + @Override + public Descriptor descriptor() { + return metadata.descriptor(); + } + + @Override + public Optional instance() { + if (lazyInstance.isLoaded()) { + return lazyInstance.get(); + } + if (lock.isHeldByCurrentThread()) { + throw new ServiceRegistryException("Cyclic dependency, attempting to obtain an instance of " + + metadata.descriptor().serviceType().fqName() + + " while instantiating it"); + } + try { + lock.lock(); + return lazyInstance.get(); + } finally { + lock.unlock(); + } + } + + @Override + public double weight() { + return metadata.weight(); + } + + @Override + public TypeName descriptorType() { + return metadata.descriptorType(); + } + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistryManager.java b/service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistryManager.java new file mode 100644 index 00000000000..1c8f1a8aa28 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/CoreServiceRegistryManager.java @@ -0,0 +1,72 @@ +/* + * 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.registry; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class CoreServiceRegistryManager implements ServiceRegistryManager { + private final ReentrantReadWriteLock lifecycleLock = new ReentrantReadWriteLock(); + private final ServiceRegistryConfig config; + private final ServiceDiscovery discovery; + + private CoreServiceRegistry registry; + + CoreServiceRegistryManager(ServiceRegistryConfig config, ServiceDiscovery discovery) { + this.config = config; + this.discovery = discovery; + } + + @Override + public ServiceRegistry registry() { + Lock readLock = lifecycleLock.readLock(); + try { + readLock.lock(); + if (registry != null) { + return registry; + } + } finally { + readLock.unlock(); + } + + Lock writeLock = lifecycleLock.writeLock(); + try { + writeLock.lock(); + if (registry != null) { + return registry; + } + + registry = new CoreServiceRegistry(config, discovery); + + return registry; + } finally { + writeLock.unlock(); + } + } + + @Override + public void shutdown() { + Lock lock = lifecycleLock.writeLock(); + try { + lock.lock(); + registry = null; + } finally { + lock.unlock(); + } + } + +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/DependencyBlueprint.java b/service/registry/src/main/java/io/helidon/service/registry/DependencyBlueprint.java new file mode 100644 index 00000000000..7f9497676f7 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/DependencyBlueprint.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.registry; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; +import io.helidon.common.GenericType; +import io.helidon.common.types.TypeName; + +/** + * Dependency metadata. + * The basic dependency supports other services to be passed to a constructor parameter. + * The dependency may be a contract, {@link java.util.List} of contracts, or an {@link java.util.Optional} + * of contract, or {@link java.util.function.Supplier} of any of these. + */ +@Prototype.Blueprint +interface DependencyBlueprint { + /** + * Type name of the service that uses this dependency. + * + * @return the service declaring this dependency + */ + TypeName service(); + + /** + * Name of the constructor parameter. + * + * @return unique name of the parameter + */ + String name(); + + /** + * Each dependency ia a specific contract. Each service provides one or more contracts for dependencies. + * For example for {@code List}, the contract is {@code MyService}. + * + * @return contract of the service we depend on + */ + @Option.Redundant + TypeName contract(); + + /** + * Generic type equivalent to {@link Dependency#contract()}. We need both, to prevent reflection at runtime. + * + * @return generic type of the dependency + */ + @Option.Redundant + @Option.Default("OBJECT") + GenericType contractType(); + + /** + * Descriptor declaring this dependency. + * Descriptor is always public. + * + * @return descriptor + */ + @Option.Redundant + TypeName descriptor(); + + /** + * Field name that declares this dependency in the {@link Dependency#descriptor()}. Can be used for code generation. + * This field is always a public constant. + * + * @return field that has the dependency on the descriptor type + */ + @Option.Redundant + String descriptorConstant(); + + /** + * Type of the dependency (exact parameter type with all generics). + * + * @return type of the dependency as {@link io.helidon.common.types.TypeName} + */ + @Option.Redundant(stringValue = false) + TypeName typeName(); +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/DependencyContext.java b/service/registry/src/main/java/io/helidon/service/registry/DependencyContext.java new file mode 100644 index 00000000000..a2ca80dba69 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/DependencyContext.java @@ -0,0 +1,48 @@ +/* + * 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.registry; + +import java.util.Map; + +/** + * All data needed for creating an instance of a service. + *

    + * The context contains only the services needed for the specific location, as provided by + * {@link ServiceInfo#dependencies()}. + */ +public interface DependencyContext { + /** + * Create a new instance from a prepared map of dependencies. + * + * @param dependencies map of dependency to an instance + * @return context to use for creating instances of services + */ + static DependencyContext create(Map dependencies) { + return new DependencyContextImpl(dependencies); + } + + /** + * Obtain a parameter for a specific dependency. + * The dependency must be known in advance and provided through + * {@link ServiceInfo}. + * + * @param dependency the dependency metadata + * @param type of the parameter, for convenience, the result is cast to this type + * @return value for the parameter, this may be null if allowed + */ + T dependency(Dependency dependency); +} diff --git a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/ScrewDriver.java b/service/registry/src/main/java/io/helidon/service/registry/DependencyContextImpl.java similarity index 55% rename from examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/ScrewDriver.java rename to service/registry/src/main/java/io/helidon/service/registry/DependencyContextImpl.java index 4b0e9420900..b6b21a47f85 100644 --- a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/ScrewDriver.java +++ b/service/registry/src/main/java/io/helidon/service/registry/DependencyContextImpl.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,27 +14,20 @@ * limitations under the License. */ -package io.helidon.examples.inject.interceptors; +package io.helidon.service.registry; -import jakarta.inject.Singleton; +import java.util.Map; -@Singleton -class ScrewDriver implements TurningTool { +class DependencyContextImpl implements DependencyContext { + private final Map dependencies; - @Override - public String name() { - return "Screw Driver"; + DependencyContextImpl(Map dependencies) { + this.dependencies = dependencies; } - @Turn + @SuppressWarnings("unchecked") @Override - public void turn(String direction) { - System.out.println(name() + " turning " + direction); + public T dependency(Dependency dependency) { + return (T) dependencies.get(dependency); } - - @Override - public String toString() { - return name(); - } - } diff --git a/service/registry/src/main/java/io/helidon/service/registry/DescriptorMetadata.java b/service/registry/src/main/java/io/helidon/service/registry/DescriptorMetadata.java new file mode 100644 index 00000000000..c660dae6bb2 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/DescriptorMetadata.java @@ -0,0 +1,68 @@ +/* + * 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.registry; + +import java.util.Set; + +import io.helidon.common.types.TypeName; + +/** + * Metadata of a single service descriptor. + * This information is stored within the Helidon specific {code META-INF} services file. + */ +public interface DescriptorMetadata { + /** + * {@link #registryType()} for core services. + */ + String REGISTRY_TYPE_CORE = "core"; + + /** + * Type of registry, such as {@code core}. + * + * @return registry type this descriptor is created for + */ + String registryType(); + + /** + * Descriptor type name. + * + * @return descriptor type + */ + TypeName descriptorType(); + + /** + * Contracts of the service. + * + * @return contracts the service implements/provides. + */ + Set contracts(); + + /** + * Weight of the service. + * + * @return service weight + * @see io.helidon.common.Weight + */ + double weight(); + + /** + * Descriptor instance. + * + * @return the descriptor + */ + GeneratedService.Descriptor descriptor(); +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/GeneratedService.java b/service/registry/src/main/java/io/helidon/service/registry/GeneratedService.java new file mode 100644 index 00000000000..624b9becb40 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/GeneratedService.java @@ -0,0 +1,354 @@ +/* + * 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.registry; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import io.helidon.builder.api.Prototype; +import io.helidon.common.config.Config; +import io.helidon.common.config.ConfigException; +import io.helidon.common.config.ConfiguredProvider; +import io.helidon.common.config.NamedService; + +/** + * All types in this class are used from generated code for services. + */ +public final class GeneratedService { + private static final System.Logger PROVIDER_LOGGER = System.getLogger(Prototype.class.getName() + ".provider"); + + /** + * Special configuration key that can be defined on provided options (loaded through ServiceLoader) that defines + * the mapping to a provider. + * Type of service, used to map to {@link io.helidon.common.config.ConfiguredProvider#configKey()}, which is the + * default "type" of a configured provider. It is then used in {@link io.helidon.common.config.NamedService#type()}, + * to allow multiple instances of the same "type" with different "name". + */ + private static final String KEY_SERVICE_TYPE = "type"; + /** + * Special configuration key that can be defined on provided options (loaded through ServiceLoader) that defines + * the name of an instance. + * It is then used in {@link io.helidon.common.config.NamedService#name()} + * to allow multiple instances of the same "type" with different "name". + * In case of object type configurations, name is taken from the configuration node name. + */ + private static final String KEY_SERVICE_NAME = "name"; + /** + * Special configuration key that can be defined on provided options (loaded through ServiceLoader) that defines + * whether a service provider of the type is enabled. + * Each service from a {@link io.helidon.common.config.ConfiguredProvider} can be enabled/disabled through + * configuration. If marked as {@code enabled = false}, the service will be ignored and not added. + */ + private static final String KEY_SERVICE_ENABLED = "enabled"; + + private GeneratedService() { + } + + /** + * Used to discover services from {@link io.helidon.service.registry.ServiceRegistry} for builder options annotated + * with {@link io.helidon.builder.api.Option.Provider}, if the blueprint is annotated with + * {@link io.helidon.builder.api.Prototype.RegistrySupport}. + * + * @param config configuration of the option + * @param configKey configuration key associated with this option + * @param serviceRegistry service registry instance + * @param providerType type of the service provider (contract) + * @param configType type of the configuration + * @param allFromRegistry whether to use all services from the registry + * @param existingValues existing values that was explicitly configured by the user + * @return instances from the user augmented with instances from the registry + * @param type of the service + */ + public static List discoverServices(Config config, + String configKey, + ServiceRegistry serviceRegistry, + Class> providerType, + Class configType, + boolean allFromRegistry, + List existingValues) { + + // type and name is a unique identification of a service - for services already defined on the builder + // do not add them from configuration (as this would duplicate service instances) + Set ignoredServices = new HashSet<>(); + existingValues.forEach(it -> ignoredServices.add(new TypeAndName(it.type(), it.name()))); + + boolean discoverServices = config.get(configKey + "-discover-services").asBoolean().orElse(allFromRegistry); + Config providersConfig = config.get(configKey); + + List configuredServices = new ArrayList<>(); + + // all child nodes of the current node + List serviceConfigList = providersConfig.asNodeList() + .orElseGet(List::of); + boolean isList = providersConfig.isList(); + + for (Config serviceConfig : serviceConfigList) { + configuredServices.add(configuredService(serviceConfig, isList)); + } + + // now we have all service configurations, we can start building up instances + if (providersConfig.isList()) { + // driven by order of declaration in config + return servicesFromList(serviceRegistry, + providerType, + configType, + configuredServices, + discoverServices, + ignoredServices); + } else { + // driven by service loader order + return servicesFromObject(providersConfig, + serviceRegistry, + providerType, + configType, + configuredServices, + discoverServices, + ignoredServices); + } + } + + /** + * Used to discover service from {@link io.helidon.service.registry.ServiceRegistry} for builder options annotated + * with {@link io.helidon.builder.api.Option.Provider}, if the blueprint is annotated with + * {@link io.helidon.builder.api.Prototype.RegistrySupport}. + * + * @param config configuration of the option + * @param configKey configuration key associated with this option + * @param serviceRegistry service registry instance + * @param providerType type of the service provider (contract) + * @param configType type of the configuration + * @param discoverServices whether to discover services from registry + * @param existingValue existing value that was explicitly configured by the user + * @return an instance, if available in the registry, or if provided by the user (user's value wins) + * @param type of the service + */ + public static Optional + discoverService(Config config, + String configKey, + ServiceRegistry serviceRegistry, + Class> providerType, + Class configType, + boolean discoverServices, + Optional existingValue) { + + // there is an explicit configuration for this service, ignore configuration + if (existingValue.isPresent()) { + return Optional.empty(); + } + + // all child nodes of the current node + List serviceConfigList = config.get(configKey).asNodeList() + .orElseGet(List::of); + + // if more than one is configured in config, fail + // if more than one exists in service loader, use the first one + if (serviceConfigList.size() > 1) { + throw new ConfigException("There can only be one provider configured for " + config.key()); + } + + List services = discoverServices(config, + configKey, + serviceRegistry, + providerType, + configType, + discoverServices, + List.of()); + + return services.isEmpty() ? Optional.empty() : Optional.of(services.getFirst()); + } + + private static ConfiguredService configuredService(Config serviceConfig, boolean isList) { + if (isList) { + // order is significant + String type = serviceConfig.get(KEY_SERVICE_TYPE).asString().orElse(null); + String name = serviceConfig.get(KEY_SERVICE_NAME).asString().orElse(type); + boolean enabled = serviceConfig.get(KEY_SERVICE_ENABLED).asBoolean().orElse(true); + + Config usedConfig = serviceConfig; + if (type == null) { + // nested approach (we are on the first node of the list, we need to go deeper) + List configs = serviceConfig.asNodeList().orElseGet(List::of); + if (configs.size() != 1) { + throw new ConfigException( + "Service provider configuration defined as a list must have a single node that is the type, " + + "with children containing the provider configuration. Failed on: " + serviceConfig.key()); + } + usedConfig = configs.get(0); + name = usedConfig.name(); + type = usedConfig.get(KEY_SERVICE_TYPE).asString().orElse(name); + enabled = usedConfig.get(KEY_SERVICE_ENABLED).asBoolean().orElse(enabled); + } + return new ConfiguredService(new TypeAndName(type, name), usedConfig, enabled); + } + // just collect each node, order will be determined by weight + + String name = serviceConfig.name(); // name is the config node name for object types + String type = serviceConfig.get(KEY_SERVICE_TYPE).asString().orElse(name); + boolean enabled = serviceConfig.get(KEY_SERVICE_ENABLED).asBoolean().orElse(true); + + return new ConfiguredService(new TypeAndName(type, name), serviceConfig, enabled); + } + + private static List + servicesFromList(ServiceRegistry serviceRegistry, + Class> providerType, + Class configType, + List configuredServices, + boolean allFromServiceLoader, + Set ignoredServices) { + Map> allProvidersByType = new HashMap<>(); + Map> unusedProvidersByType = new LinkedHashMap<>(); + + serviceRegistry.all(providerType) + .forEach(provider -> { + allProvidersByType.put(provider.configKey(), provider); + unusedProvidersByType.put(provider.configKey(), provider); + }); + + List result = new ArrayList<>(); + + // first add all configured + for (ConfiguredService service : configuredServices) { + TypeAndName typeAndName = service.typeAndName(); + if (!ignoredServices.add(typeAndName)) { + unusedProvidersByType.remove(typeAndName.type()); + + if (PROVIDER_LOGGER.isLoggable(System.Logger.Level.DEBUG)) { + PROVIDER_LOGGER.log(System.Logger.Level.DEBUG, "Service: " + typeAndName + + " is already added in builder, ignoring configured one."); + } + + continue; + } + ConfiguredProvider provider = allProvidersByType.get(typeAndName.type()); + if (provider == null) { + throw new ConfigException("Unknown provider configured. Expecting a provider with type \"" + typeAndName.type() + + "\", but only the following providers are supported: " + + allProvidersByType.keySet() + ", " + + "provider interface: " + providerType.getName() + + ", configured service: " + configType.getName()); + } + unusedProvidersByType.remove(typeAndName.type()); + if (service.enabled()) { + result.add(provider.create(service.serviceConfig(), typeAndName.name())); + } + } + + // then (if desired) add the rest + if (allFromServiceLoader) { + unusedProvidersByType.forEach((type, provider) -> { + if (ignoredServices.add(new TypeAndName(type, type))) { + result.add(provider.create(Config.empty(), type)); + } + }); + } + + return result; + } + + private static List + servicesFromObject(Config providersConfig, + ServiceRegistry serviceRegistry, + Class> providerType, + Class configType, + List configuredServices, + boolean allFromServiceLoader, + Set ignoredServices) { + // order is determined by service loader + Set availableProviders = new HashSet<>(); + Map allConfigs = new HashMap<>(); + configuredServices.forEach(it -> allConfigs.put(it.typeAndName().type, it)); + Set unusedConfigs = new HashSet<>(allConfigs.keySet()); + + List result = new ArrayList<>(); + + List> all = serviceRegistry.all(providerType); + for (ConfiguredProvider provider : all) { + ConfiguredService configuredService = allConfigs.get(provider.configKey()); + availableProviders.add(provider.configKey()); + unusedConfigs.remove(provider.configKey()); + if (configuredService == null) { + if (allFromServiceLoader) { + // even though the specific key does not exist, we want to have the real config tree, so we can get to the + // root of it + // when there is no configuration, the name defaults to the type + String type = provider.configKey(); + if (ignoredServices.add(new TypeAndName(type, type))) { + result.add(provider.create(providersConfig.get(type), type)); + } else { + if (PROVIDER_LOGGER.isLoggable(System.Logger.Level.DEBUG)) { + PROVIDER_LOGGER.log(System.Logger.Level.DEBUG, "Service: " + new TypeAndName(type, type) + + " is already added in builder, ignoring configured one."); + } + } + } + } else { + if (configuredService.enabled()) { + if (ignoredServices.add(configuredService.typeAndName())) { + result.add(provider.create(configuredService.serviceConfig(), + configuredService.typeAndName().name())); + } else { + if (PROVIDER_LOGGER.isLoggable(System.Logger.Level.DEBUG)) { + PROVIDER_LOGGER.log(System.Logger.Level.DEBUG, "Service: " + configuredService.typeAndName() + + " is already added in builder, ignoring configured one."); + } + } + } + } + } + + if (!unusedConfigs.isEmpty()) { + throw new ConfigException("Unknown provider configured. Expected providers with types: " + unusedConfigs + + ", but only the following providers are supported: " + availableProviders + + ", provider interface: " + providerType.getName() + + ", configured service: " + configType.getName()); + } + return result; + } + + /** + * A descriptor of a service. In addition to providing service metadata, this also allows instantiation + * of the service instance, with dependent services as parameters. + * + * @param type of the described service + */ + public interface Descriptor extends ServiceInfo { + /** + * Create a new service instance. + * + * @param ctx dependency context with all dependencies of this service + * @return a new instance, must be of the type T or a subclass + */ + // we cannot return T, as it does not allow us to correctly handle inheritance + default Object instantiate(DependencyContext ctx) { + throw new IllegalStateException("Cannot instantiate type " + serviceType().fqName() + ", as it is either abstract," + + " or an interface."); + } + } + + private record TypeAndName(String type, String name) { + } + + private record ConfiguredService(TypeAndName typeAndName, Config serviceConfig, boolean enabled) { + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/GlobalServiceRegistry.java b/service/registry/src/main/java/io/helidon/service/registry/GlobalServiceRegistry.java new file mode 100644 index 00000000000..33e4982f209 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/GlobalServiceRegistry.java @@ -0,0 +1,118 @@ +/* + * 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.registry; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +import io.helidon.common.config.GlobalConfig; + +/** + * A global singleton manager for a service registry. + *

    + * Note that when using this registry, testing is a bit more complicated, as the registry is shared + * statically. + */ +public final class GlobalServiceRegistry { + private static final AtomicReference INSTANCE = new AtomicReference<>(); + private static final ReadWriteLock RW_LOCK = new ReentrantReadWriteLock(); + + private GlobalServiceRegistry() { + } + + /** + * Whether a service registry instance is configured. + * + * @return {@code true} if a registry instance was already created + */ + public static boolean configured() { + return INSTANCE.get() != null; + } + + /** + * Current global service registry, will create a new instance if one is not configured. + * + * @return global service registry + */ + public static ServiceRegistry registry() { + try { + RW_LOCK.readLock().lock(); + ServiceRegistry currentInstance = INSTANCE.get(); + if (currentInstance != null) { + return currentInstance; + } + } finally { + RW_LOCK.readLock().unlock(); + } + try { + RW_LOCK.writeLock().lock(); + ServiceRegistryConfig config; + if (GlobalConfig.configured()) { + config = ServiceRegistryConfig.create(GlobalConfig.config().get("registry")); + } else { + config = ServiceRegistryConfig.create(); + } + ServiceRegistry newInstance = ServiceRegistryManager.create(config).registry(); + INSTANCE.set(newInstance); + return newInstance; + } finally { + RW_LOCK.writeLock().unlock(); + } + } + + /** + * Current global registry if configured, will replace the current global registry with the + * one provided by supplier if none registered. + * + * @param registrySupplier supplier of new global registry if not yet configured + * @return global service registry + */ + public static ServiceRegistry registry(Supplier registrySupplier) { + try { + RW_LOCK.readLock().lock(); + ServiceRegistry currentInstance = INSTANCE.get(); + if (currentInstance != null) { + return currentInstance; + } + } finally { + RW_LOCK.readLock().unlock(); + } + try { + RW_LOCK.writeLock().lock(); + ServiceRegistry newInstance = registrySupplier.get(); + INSTANCE.set(newInstance); + return newInstance; + } finally { + RW_LOCK.writeLock().unlock(); + } + } + + /** + * Set the current global registry. + * This method always returns the same instance as provided, though the next call to + * {@link #registry()} may return a different instance if the instance is replaced by another thread. + * + * @param newGlobalRegistry global registry to set + * @return the same instance + */ + public static ServiceRegistry registry(ServiceRegistry newGlobalRegistry) { + INSTANCE.set(newGlobalRegistry); + return newGlobalRegistry; + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/Service.java b/service/registry/src/main/java/io/helidon/service/registry/Service.java new file mode 100644 index 00000000000..58da92b3168 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/Service.java @@ -0,0 +1,157 @@ +/* + * 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.registry; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.helidon.common.Weighted; +import io.helidon.common.types.TypeName; + +/** + * A set of annotations (and APIs) required to declare a service. + */ +public final class Service { + private Service() { + } + + /** + * A service provider. This is similar to {@link java.util.ServiceLoader} service providers. + *

    + * The provider is an implementation of a {@link io.helidon.service.registry.Service.Contract} that is discoverable by + * the service registry. + * A service provider annotated with this annotation must provide either of: + *

      + *
    • an accessible no-argument constructor + * (package private is sufficient), and must itself be at least package private
    • + *
    • an accessible constructor with arguments that are all services as well
    • + *
    + * + * The support for providing constructor arguments is limited to the following types: + *
      + *
    • {@code Contract} - obtain an instance of a service providing that contract
    • + *
    • {@code Optional} - the other service may not be available
    • + *
    • {@code List} - obtain all instances of services providing the contract
    • + *
    • {@code Supplier} - and suppliers of all above, to break instantiation chaining, and to support cyclic + * service references, just make sure you call the {@code get} method outside of the constructor
    • + *
    + * + * A service provider may implement the contract in two ways: + *
      + *
    • Direct implementation of interface (or extending an abstract class)
    • + *
    • Implementing a {@link java.util.function.Supplier} of the contract; when using supplier, service registry + * supports the capability to return {@link java.util.Optional} in case the service cannot provide a value; such + * a service will be ignored and only other implementations (with lower weight) would be used
    • + *
    + */ + @Documented + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.TYPE) + public @interface Provider { + /** + * Type name of this annotation. + */ + TypeName TYPE = TypeName.create(Provider.class); + } + + /** + * The {@code Contract} annotation is used to relay significance to the type that it annotates. While remaining optional in + * its use, it is typically placed on an interface definition to signify that the given type can be used for lookup in the + * service registry. + * While normally placed on interface types, it can also be placed on abstract and concrete class as well. The main point is + * that a {@code Contract} is the focal point for service lookup. + *

    + * If the developer does not have access to the source to place this annotation on the interface definition directly then + * consider using {@link ExternalContracts} instead - this annotation can be placed on the + * implementation class implementing the given {@code Contract} interface(s). + *

    + * Default behavior of the service registry is to only provide support lookup based on contracts. + */ + @Documented + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.TYPE) + public @interface Contract { + } + + /** + * Placed on the implementation of a service as an alternative to using a {@link Contract}. + *

    + * Use this annotation when it is impossible to place an annotation on the interface itself - for instance of the interface + * comes from a 3rd party library provider. + */ + @Documented + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.TYPE) + public @interface ExternalContracts { + + /** + * The advertised contract type(s) for the service class implementation. + * + * @return the external contract(s) + */ + Class[] value(); + } + + /** + * Annotation to add a custom service descriptor. + *

    + * The service descriptor will be added as any other service descriptor that is generated, only it is expected + * as a source. + * The descriptor MUST follow the approach of Helidon service descriptor, and must be public, + * provide a public constant called {@code INSTANCE}, and all its dependencies + * ({@link io.helidon.service.registry.Dependency} + * must be available as public constants (and correctly described in each Ip instance). + */ + @Documented + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) + public @interface Descriptor { + /** + * Type name of this interface. + */ + TypeName TYPE = TypeName.create(Descriptor.class); + + /** + * Type of service registry that should read this descriptor. Defaults to + * {@value DescriptorMetadata#REGISTRY_TYPE_CORE}, so the descriptor must only implement + * {@link io.helidon.service.registry.GeneratedService.Descriptor}. + * + * @return type of registry this descriptor supports + */ + String registryType() default DescriptorMetadata.REGISTRY_TYPE_CORE; + + /** + * The weight of the service. This is required for predefined descriptors, as we do not want + * to instantiate them unless really needed. + * + * @return weight of this descriptor + */ + double weight() default Weighted.DEFAULT_WEIGHT; + + /** + * Contracts of this service descriptor. + * Normally these are discovered from the service provider type, but as this is a pre-built descriptor, + * we need to get them through annotation. + * + * @return contracts of this service descriptor + */ + Class[] contracts(); + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceDiscovery.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceDiscovery.java new file mode 100644 index 00000000000..57cacc8337b --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceDiscovery.java @@ -0,0 +1,74 @@ +/* + * 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.registry; + +import java.util.List; + +/** + * Access to discovered service metadata. + */ +public interface ServiceDiscovery { + /** + * A file that contains all services within a module. Each service has an identifier of the registry that + * created it (such as {@code core}), the class of the service descriptor, + * weight of the service, and a list of contracts it implements. + *

    + * This file is used by {@link io.helidon.service.registry.ServiceDiscovery} to find services. + */ + String SERVICES_RESOURCE = "META-INF/helidon/service.registry"; + /** + * A file that contains all service provider interfaces that expose a service for + * Java {@link java.util.ServiceLoader} that is used to tell Helidon Service Registry to add implementations to + * the registry to be discoverable in addition to services defined in {@link #SERVICES_RESOURCE}. + */ + String SERVICES_LOADER_RESOURCE = "META-INF/helidon/service.loader"; + + /** + * Create a new instance that discovers service descriptors from classpath. + * + * @return service discovery based on classpath + */ + static ServiceDiscovery create() { + return create(ServiceRegistryConfig.create()); + } + + /** + * Create a new instance that discovers service descriptors based on the configuration. + * + * @param config registry configuration to control discovery + * @return service discovery based on classpath + */ + static ServiceDiscovery create(ServiceRegistryConfig config) { + return CoreServiceDiscovery.create(config); + } + + /** + * Service discovery that does not discover anything. + * + * @return a no-op service discovery + */ + static ServiceDiscovery noop() { + return CoreServiceDiscovery.noop(); + } + + /** + * All discovered metadata of this service discovery. + * + * @return all discovered metadata + */ + List allMetadata(); +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceInfo.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceInfo.java new file mode 100644 index 00000000000..50ffcf50e83 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceInfo.java @@ -0,0 +1,72 @@ +/* + * 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.registry; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Weighted; +import io.helidon.common.types.TypeName; + +/** + * Service metadata. + */ +public interface ServiceInfo extends Weighted { + /** + * Type of the service this descriptor describes. + * + * @return service type + */ + TypeName serviceType(); + + /** + * Type of the service descriptor (usually generated). + * + * @return descriptor type + */ + TypeName descriptorType(); + + /** + * Set of contracts the described service implements. + * + * @return set of contracts + */ + default Set contracts() { + return Set.of(); + } + + /** + * List of dependencies required by this service. + * Each dependency is a point of injection of a dependency into + * a constructor. + * + * @return required dependencies + */ + default List dependencies() { + return List.of(); + } + + /** + * Returns {@code true} for abstract classes and interfaces, + * returns {@code false} by default. + * + * @return whether this descriptor describes an abstract class or interface + */ + default boolean isAbstract() { + return false; + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceLoader__ServiceDescriptor.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceLoader__ServiceDescriptor.java new file mode 100644 index 00000000000..56bb3769c04 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceLoader__ServiceDescriptor.java @@ -0,0 +1,123 @@ +/* + * 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.registry; + +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +import io.helidon.common.LazyValue; +import io.helidon.common.types.TypeName; + +/** + * Service descriptor to enable dependency on services loaded via {@link java.util.ServiceLoader}. + */ +@SuppressWarnings("checkstyle:TypeName") // matches pattern of generated descriptors +public abstract class ServiceLoader__ServiceDescriptor implements GeneratedService.Descriptor { + private static final TypeName DESCRIPTOR_TYPE = TypeName.create(ServiceLoader__ServiceDescriptor.class); + + // we must use instance comparison, so we must make sure we give the same instance for the same combination + private static final Map INSTANCE_CACHE = new HashMap<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); + + private ServiceLoader__ServiceDescriptor() { + } + + /** + * Create a new instance for a specific service provider interface and its implementation. + * + * @param providerInterface provider interface type + * @param provider Java Service Loader provider instance + * @param weight weight of the provider + * @return new descriptor + */ + public static GeneratedService.Descriptor create(TypeName providerInterface, + ServiceLoader.Provider provider, + double weight) { + LOCK.lock(); + try { + TypeName providerImpl = TypeName.create(provider.type()); + ProviderKey key = new ProviderKey(providerInterface, providerImpl); + ServiceLoader__ServiceDescriptor descriptor = INSTANCE_CACHE.get(key); + if (descriptor == null) { + descriptor = new ServiceProviderDescriptor(providerInterface, providerImpl, provider, weight); + INSTANCE_CACHE.put(key, descriptor); + } + + return descriptor; + } finally { + LOCK.unlock(); + } + } + + @Override + public TypeName descriptorType() { + return DESCRIPTOR_TYPE; + } + + private static class ServiceProviderDescriptor extends ServiceLoader__ServiceDescriptor { + private final TypeName providerInterface; + private final Set contracts; + private final TypeName providerImpl; + private final double weight; + private final LazyValue instance; + + private ServiceProviderDescriptor(TypeName providerInterface, + TypeName providerImpl, + ServiceLoader.Provider provider, + double weight) { + this.providerInterface = providerInterface; + this.contracts = Set.of(providerInterface); + this.providerImpl = providerImpl; + this.weight = weight; + this.instance = LazyValue.create(provider); + } + + @Override + public String toString() { + return providerInterface + "/" + + providerImpl + + "(" + weight + ")"; + } + + @Override + public TypeName serviceType() { + return providerImpl; + } + + @Override + public Set contracts() { + return contracts; + } + + @Override + public Object instantiate(DependencyContext ctx) { + return instance.get(); + } + + @Override + public double weight() { + return weight; + } + + } + + private record ProviderKey(TypeName providerInterface, TypeName providerImpl) { + } +} diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Big.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceMetadata.java similarity index 63% rename from examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Big.java rename to service/registry/src/main/java/io/helidon/service/registry/ServiceMetadata.java index 0ab0287d556..b77781742bf 100644 --- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/Big.java +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceMetadata.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,18 +14,17 @@ * limitations under the License. */ -package io.helidon.examples.inject.basics; +package io.helidon.service.registry; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.Set; -import jakarta.inject.Qualifier; +import io.helidon.common.types.TypeName; +import io.helidon.service.registry.GeneratedService.Descriptor; -/** - * Custom annotation. - */ -@Qualifier -@Retention(RetentionPolicy.CLASS) -public @interface Big { +interface ServiceMetadata { + double weight(); + + Set contracts(); + Descriptor descriptor(); } diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry.java new file mode 100644 index 00000000000..2bf35bf26fb --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry.java @@ -0,0 +1,209 @@ +/* + * 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.registry; + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import io.helidon.common.types.TypeName; + +/** + * Entry point to services in Helidon. + *

    + * The service registry has knowledge about all the services within your application. + */ +@Service.Contract +public interface ServiceRegistry { + /** + * Type name of this interface. + */ + TypeName TYPE = TypeName.create(ServiceRegistry.class); + + /** + * Get the first service instance matching the contract with the expectation that there is a match available. + * + * @param contract contract to look-up + * @param type of the contract + * @return the best service instance matching the contract + * @throws io.helidon.service.registry.ServiceRegistryException if there is no service that could satisfy the lookup, or the + * resolution to instance failed + */ + default T get(Class contract) { + return get(TypeName.create(contract)); + } + + /** + * Get the first service instance matching the contract with the expectation that there is a match available. + * + * @param contract contract to look-up + * @param type of the contract (we will "blindly" cast the result to the expected type, make sure you use the right + * one) + * @return the best service instance matching the contract + * @throws io.helidon.service.registry.ServiceRegistryException if there is no service that could satisfy the lookup, or the + * resolution to instance failed + */ + T get(TypeName contract); + + /** + * Get the first service instance matching the contract with the expectation that there may not be a match available. + * + * @param contract contract to look-up + * @param type of the contract + * @return the best service instance matching the contract, or an empty {@link java.util.Optional} if none match + * @throws io.helidon.service.registry.ServiceRegistryException if there is no service that could satisfy the lookup, or the + * resolution to instance failed + */ + default Optional first(Class contract) { + return first(TypeName.create(contract)); + } + + /** + * Get the first service instance matching the contract with the expectation that there may not be a match available. + * + * @param contract contract to look-up + * @param type of the contract + * @return the best service instance matching the contract, or an empty {@link java.util.Optional} if none match + * @throws io.helidon.service.registry.ServiceRegistryException if there is no service that could satisfy the lookup, or the + * resolution to instance failed + */ + Optional first(TypeName contract); + + /** + * Get all service instances matching the contract with the expectation that there may not be a match available. + * + * @param contract contract to look-up + * @param type of the contract + * @return list of services matching the criteria, may be empty if none matched, or no instances were provided + */ + default List all(Class contract) { + return all(TypeName.create(contract)); + } + + /** + * Get all service instances matching the contract with the expectation that there may not be a match available. + * + * @param contract contract to look-up + * @param type of the contract + * @return list of services matching the criteria, may be empty if none matched, or no instances were provided + */ + List all(TypeName contract); + + /** + * Get the first service supplier matching the contract with the expectation that there is a match available. + * The provided {@link java.util.function.Supplier#get()} may throw an + * {@link io.helidon.service.registry.ServiceRegistryException} in case the matching service cannot provide a value (either + * because of scope mismatch, or because an instance was not provided by the service provider. + * + * @param contract contract to find + * @param type of the contract + * @return the best service supplier matching the lookup + * @throws io.helidon.service.registry.ServiceRegistryException if there is no service that could satisfy the lookup + */ + default Supplier supply(Class contract) { + return supply(TypeName.create(contract)); + } + + /** + * Get the first service supplier matching the contract with the expectation that there is a match available. + * The provided {@link java.util.function.Supplier#get()} may throw an + * {@link io.helidon.service.registry.ServiceRegistryException} in case the matching service cannot provide a value (either + * because of scope mismatch, or because an instance was not provided by the service provider. + * + * @param contract contract to find + * @param type of the contract + * @return the best service supplier matching the lookup + * @throws io.helidon.service.registry.ServiceRegistryException if there is no service that could satisfy the lookup + */ + Supplier supply(TypeName contract); + + /** + * Get the first service supplier matching the contract with the expectation that there may not be a match available. + * + * @param contract contract we look for + * @param type of the contract + * @return supplier of an optional instance + */ + default Supplier> supplyFirst(Class contract) { + return supplyFirst(TypeName.create(contract)); + } + + /** + * Get the first service supplier matching the contract with the expectation that there may not be a match available. + * + * @param contract contract we look for + * @param type of the contract + * @return supplier of an optional instance + */ + Supplier> supplyFirst(TypeName contract); + + /** + * Lookup a supplier of a list of instances of the requested contract, with the expectation that there may not be a + * match available. + * + * @param contract contract we look for + * @param type of the contract + * @return a supplier of list of instances + */ + default Supplier> supplyAll(Class contract) { + return supplyAll(TypeName.create(contract)); + } + + /** + * Lookup a supplier of a list of instances of the requested contract, with the expectation that there may not be a + * match available. + * + * @param contract contract we look for + * @param type of the contract + * @return a supplier of list of instances + */ + Supplier> supplyAll(TypeName contract); + + /** + * Provide a value for a specific service info instance. + * This method uses instance equality for service info, so be careful to use the singleton instance + * from the service descriptor, or instances provided by {@link #allServices(Class)}. + * + * @param serviceInfo service info instance + * @return value of the service described by the service info provided (always a single value), as there is support + * for providers that are {@link java.util.function.Supplier} of an instance, and that may return + * {@link java.util.Optional}, we may not get a value, hence we return {@link java.util.Optional} as well + * @param type of the expected instance, we just cast to it, so this may cause runtime issues if assigned to invalid + * type + */ + Optional get(ServiceInfo serviceInfo); + + /** + * Get all services for a specific contract. The list may be empty if there are no services available. + * To get an instance, use {@link #get(ServiceInfo)}. + * + * @param contract contract we look for + * @return list of service metadata of services that satisfy the provided contract + */ + default List allServices(Class contract) { + return allServices(TypeName.create(contract)); + } + + /** + * Get all services for a specific contract. The list may be empty if there are no services available. + * To get an instance, use {@link #get(ServiceInfo)}. + * + * @param contract contract we look for + * @return list of service metadata of services that satisfy the provided contract + */ + List allServices(TypeName contract); +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigBlueprint.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigBlueprint.java new file mode 100644 index 00000000000..607f3bc8a4a --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigBlueprint.java @@ -0,0 +1,86 @@ +/* + * 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.registry; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; +import io.helidon.common.config.Config; +import io.helidon.service.registry.GeneratedService.Descriptor; + +/** + * Helidon service registry configuration. + */ +@Prototype.Blueprint +@Prototype.Configured("registry") +@Prototype.CustomMethods(ServiceRegistryConfigSupport.CustomMethods.class) +interface ServiceRegistryConfigBlueprint { + /** + * Whether to discover services from the class path. + * When set to {@code false}, only services added through {@link #serviceDescriptors()} and/or + * {@link #serviceInstances()} would be available. + * + * @return whether to discover services from classpath, defaults to {@code true} + */ + @Option.Configured + @Option.DefaultBoolean(true) + boolean discoverServices(); + + /** + * Whether to discover services from Java service loader. + * See {@link io.helidon.service.registry.ServiceDiscovery#SERVICES_LOADER_RESOURCE}. + * + * @return whether to discover Java {@link java.util.ServiceLoader} services from classpath (a curated list only), + * defaults to {@code true} + */ + @Option.Configured + @Option.DefaultBoolean(true) + boolean discoverServicesFromServiceLoader(); + + /** + * Manually registered service descriptors to add to the registry. + * This is useful when {@link #discoverServices()} is set to {@code false}, to register only hand-picked services + * into the registry. + *

    + * Even when service discovery is used, this can be used to add service descriptors that are not part of + * a service discovery mechanism (such as testing services). + * + * @return services to register + */ + @Option.Singular + List> serviceDescriptors(); + + /** + * Manually register initial bindings for some of the services in the registry. + * + * @return service instances to register + */ + @Option.Singular + @Option.SameGeneric + Map, Object> serviceInstances(); + + /** + * Config instance used to configure this registry configuration. + * DO NOT USE for application configuration! + * + * @return config node used to configure this service registry config instance (if any) + */ + Optional config(); +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigSupport.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigSupport.java new file mode 100644 index 00000000000..15e9af6a88f --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryConfigSupport.java @@ -0,0 +1,115 @@ +/* + * 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.registry; + +import java.util.Objects; +import java.util.Set; + +import io.helidon.builder.api.Prototype; +import io.helidon.common.Weighted; +import io.helidon.common.types.TypeName; + +class ServiceRegistryConfigSupport { + private ServiceRegistryConfigSupport() { + } + + static class CustomMethods { + private CustomMethods() { + } + + /** + * Put an instance of a contract outside of service described services. + * This will create a "virtual" service descriptor that will not be valid for metadata operations. + * + * @param builder ignored + * @param contract contract to add a specific instance for + * @param instance instance of the contract + */ + @Prototype.BuilderMethod + static void putContractInstance(ServiceRegistryConfig.BuilderBase builder, + TypeName contract, + Object instance) { + builder.putServiceInstance(new VirtualDescriptor(contract), instance); + } + + /** + * Put an instance of a contract outside of service described services. + * This will create a "virtual" service descriptor that will not be valid for metadata operations. + * + * @param builder ignored + * @param contract contract to add a specific instance for + * @param instance instance of the contract + */ + @Prototype.BuilderMethod + static void putContractInstance(ServiceRegistryConfig.BuilderBase builder, + Class contract, + Object instance) { + putContractInstance(builder, TypeName.create(contract), instance); + } + } + + private static class VirtualDescriptor implements GeneratedService.Descriptor { + private static final TypeName TYPE = TypeName.create(VirtualDescriptor.class); + private final Set contracts; + private final TypeName serviceType; + private final TypeName descriptorType; + + private VirtualDescriptor(TypeName contract) { + this.contracts = Set.of(contract); + this.serviceType = contract; + this.descriptorType = TypeName.builder(TYPE) + .className(TYPE.className() + "_" + contract.className() + "__VirtualDescriptor") + .build(); + } + + @Override + public TypeName serviceType() { + return serviceType; + } + + @Override + public TypeName descriptorType() { + return descriptorType; + } + + @Override + public Set contracts() { + return contracts; + } + + @Override + public double weight() { + return Weighted.DEFAULT_WEIGHT + 1000; + } + + @Override + public int hashCode() { + return Objects.hash(serviceType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VirtualDescriptor that)) { + return false; + } + return Objects.equals(serviceType, that.serviceType); + } + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryException.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryException.java new file mode 100644 index 00000000000..4c4e24621db --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryException.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.service.registry; + +/** + * An exception marking a problem with service registry operations. + */ +public class ServiceRegistryException extends RuntimeException { + /** + * Create an exception with a descriptive message. + * + * @param message the message + */ + public ServiceRegistryException(String message) { + super(message); + } + + /** + * Create an exception with a descriptive message and a cause. + * + * @param message the message + * @param cause throwable causing this exception + */ + public ServiceRegistryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManager.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManager.java new file mode 100644 index 00000000000..6dcd436db72 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManager.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. + */ + +package io.helidon.service.registry; + +/** + * Manager is responsible for managing the state of a {@link io.helidon.service.registry.ServiceRegistry}. + * Each manager instances owns a single service registry. + *

    + * To use a singleton service across application, either pass it through parameters, or use + * {@link io.helidon.service.registry.GlobalServiceRegistry}. + */ +public interface ServiceRegistryManager { + /** + * Create a new service registry manager with default configuration. + * + * @return a new service registry manager + */ + static ServiceRegistryManager create() { + return create(ServiceRegistryConfig.create()); + } + + /** + * Create a new service registry manager with custom configuration. + * + * @param config configuration of this registry manager + * @return a new configured service registry manager + */ + static ServiceRegistryManager create(ServiceRegistryConfig config) { + return ServiceRegistryManagerDiscovery.create(config); + } + + /** + * Get (or initialize and get) the service registry managed by this manager. + * + * @return service registry ready to be used + */ + ServiceRegistry registry(); + + /** + * Shutdown the managed service registry. + */ + void shutdown(); +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManagerDiscovery.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManagerDiscovery.java new file mode 100644 index 00000000000..94ea9c0d035 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistryManagerDiscovery.java @@ -0,0 +1,46 @@ +/* + * 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.registry; + +import java.util.Optional; +import java.util.ServiceLoader; + +import io.helidon.common.HelidonServiceLoader; +import io.helidon.common.LazyValue; +import io.helidon.service.registry.spi.ServiceRegistryManagerProvider; + +final class ServiceRegistryManagerDiscovery { + private static final Optional REGISTRY_MANAGER_PROVIDER = + HelidonServiceLoader.builder(ServiceLoader.load(ServiceRegistryManagerProvider.class)) + .build() + .stream() + .findFirst(); + + private ServiceRegistryManagerDiscovery() { + } + + static ServiceRegistryManager create(ServiceRegistryConfig config) { + ServiceDiscovery discovery = config.discoverServices() || config.discoverServicesFromServiceLoader() + ? CoreServiceDiscovery.create(config) + : CoreServiceDiscovery.noop(); + + LazyValue coreManager = LazyValue.create(() -> new CoreServiceRegistryManager(config, discovery)); + + return REGISTRY_MANAGER_PROVIDER.map(it -> it.create(config, discovery, coreManager)) + .orElseGet(coreManager); + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry__ServiceDescriptor.java b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry__ServiceDescriptor.java new file mode 100644 index 00000000000..e69b6b09808 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/ServiceRegistry__ServiceDescriptor.java @@ -0,0 +1,53 @@ +/* + * 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.registry; + +import java.util.Set; + +import io.helidon.common.types.TypeName; + +/** + * Service descriptor to enable dependency on {@link io.helidon.service.registry.ServiceRegistry}. + */ +@SuppressWarnings("checkstyle:TypeName") // matches pattern of generated descriptors +public class ServiceRegistry__ServiceDescriptor implements GeneratedService.Descriptor { + /** + * Singleton instance to be referenced when building applications. + */ + public static final ServiceRegistry__ServiceDescriptor INSTANCE = new ServiceRegistry__ServiceDescriptor(); + + private static final TypeName DESCRIPTOR_TYPE = TypeName.create(ServiceRegistry__ServiceDescriptor.class); + private static final Set CONTRACTS = Set.of(ServiceRegistry.TYPE); + + private ServiceRegistry__ServiceDescriptor() { + } + + @Override + public TypeName serviceType() { + return ServiceRegistry.TYPE; + } + + @Override + public TypeName descriptorType() { + return DESCRIPTOR_TYPE; + } + + @Override + public Set contracts() { + return CONTRACTS; + } +} diff --git a/service/registry/src/main/java/io/helidon/service/registry/package-info.java b/service/registry/src/main/java/io/helidon/service/registry/package-info.java new file mode 100644 index 00000000000..50b743b7f05 --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * API required to define services, and to compile the code generated sources for Helidon Service Registry, + * with a core service registry implementation (replacement for {@link java.util.ServiceLoader}). + *

    + * The following main entry point for declaring services is {@link io.helidon.service.registry.Service}. + */ +package io.helidon.service.registry; diff --git a/service/registry/src/main/java/io/helidon/service/registry/spi/ServiceRegistryManagerProvider.java b/service/registry/src/main/java/io/helidon/service/registry/spi/ServiceRegistryManagerProvider.java new file mode 100644 index 00000000000..6ffed28d3ca --- /dev/null +++ b/service/registry/src/main/java/io/helidon/service/registry/spi/ServiceRegistryManagerProvider.java @@ -0,0 +1,42 @@ +/* + * 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.registry.spi; + +import java.util.function.Supplier; + +import io.helidon.service.registry.ServiceDiscovery; +import io.helidon.service.registry.ServiceRegistryConfig; +import io.helidon.service.registry.ServiceRegistryManager; + +/** + * A {@link java.util.ServiceLoader} provider that enables a different type of service registry. + * In Helidon this could be a service registry with full injection support. + */ +public interface ServiceRegistryManagerProvider { + /** + * Create a new registry manager. + * + * @param config configuration as provided to {@link io.helidon.service.registry.ServiceRegistryManager} + * @param serviceDiscovery service discovery to load service instances + * @param coreRegistryManager core service registry manager, if it would be used as a backing one for the one provided by this + * service (lazy loaded) + * @return a new service registry manager + */ + ServiceRegistryManager create(ServiceRegistryConfig config, + ServiceDiscovery serviceDiscovery, + Supplier coreRegistryManager); +} diff --git a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/package-info.java b/service/registry/src/main/java/io/helidon/service/registry/spi/package-info.java similarity index 76% rename from examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/package-info.java rename to service/registry/src/main/java/io/helidon/service/registry/spi/package-info.java index 5acd84b9f97..e583f20e3b8 100644 --- a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/package-info.java +++ b/service/registry/src/main/java/io/helidon/service/registry/spi/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. @@ -15,6 +15,6 @@ */ /** - * Examples of Intercepted services in Injection. + * Service registry SPI provides extension points for the service registry. */ -package io.helidon.examples.inject.interceptors; +package io.helidon.service.registry.spi; diff --git a/service/registry/src/main/java/module-info.java b/service/registry/src/main/java/module-info.java new file mode 100644 index 00000000000..b1b2230390f --- /dev/null +++ b/service/registry/src/main/java/module-info.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Core service registry, supporting {@link io.helidon.service.registry.Service.Provider}. + */ +module io.helidon.service.registry { + exports io.helidon.service.registry; + exports io.helidon.service.registry.spi; + + requires transitive io.helidon.common.config; + requires transitive io.helidon.builder.api; + requires transitive io.helidon.common.types; + + uses io.helidon.service.registry.spi.ServiceRegistryManagerProvider; +} \ No newline at end of file diff --git a/service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/native-image.properties b/service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/native-image.properties new file mode 100644 index 00000000000..af807d5c318 --- /dev/null +++ b/service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/native-image.properties @@ -0,0 +1,19 @@ +# +# 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. +# +Args=--initialize-at-build-time=io.helidon.common.types \ + --initialize-at-build-time=io.helidon.service.registry.ServiceLoader__ServiceDescriptor \ + --initialize-at-build-time=io.helidon.service.registry.CoreServiceDiscovery \ + --initialize-at-build-time=io.helidon.common.Errors diff --git a/service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/resource-config.json b/service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/resource-config.json new file mode 100644 index 00000000000..aa0540496bb --- /dev/null +++ b/service/registry/src/main/resources/META-INF/native-image/io.helidon.service/helidon-service-registry/resource-config.json @@ -0,0 +1,10 @@ +{ + "resources": [ + { + "pattern": "META-INF/helidon/service.registry" + }, + { + "pattern": "META-INF/helidon/service.loader" + } + ] +} \ No newline at end of file diff --git a/service/tests/codegen/pom.xml b/service/tests/codegen/pom.xml new file mode 100644 index 00000000000..735ed76d54a --- /dev/null +++ b/service/tests/codegen/pom.xml @@ -0,0 +1,64 @@ + + + + + + io.helidon.service.tests + helidon-service-tests-project + 4.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + helidon-service-tests-codegen + Helidon Service Tests Codegen + Tests for service codegen + + + + io.helidon.service + helidon-service-codegen + + + io.helidon.service + helidon-service-registry + + + io.helidon.config + helidon-config-metadata + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + io.helidon.common.testing + helidon-common-testing-junit5 + test + + + diff --git a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Turn.java b/service/tests/codegen/src/main/java/module-info.java similarity index 72% rename from examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Turn.java rename to service/tests/codegen/src/main/java/module-info.java index d49bf1fbaee..6dfc02fed59 100644 --- a/examples/inject/interceptors/src/main/java/io/helidon/examples/inject/interceptors/Turn.java +++ b/service/tests/codegen/src/main/java/module-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. @@ -14,10 +14,8 @@ * limitations under the License. */ -package io.helidon.examples.inject.interceptors; - -import io.helidon.inject.api.InterceptedTrigger; - -@InterceptedTrigger -public @interface Turn { -} +module io.helidon.service.tests.codegen { + requires io.helidon.service.registry; + requires io.helidon.service.codegen; + requires io.helidon.config.metadata; +} \ No newline at end of file diff --git a/service/tests/codegen/src/test/java/io/helidon/service/tests/codegen/ServiceCodegenTypesTest.java b/service/tests/codegen/src/test/java/io/helidon/service/tests/codegen/ServiceCodegenTypesTest.java new file mode 100644 index 00000000000..c0ccc588d80 --- /dev/null +++ b/service/tests/codegen/src/test/java/io/helidon/service/tests/codegen/ServiceCodegenTypesTest.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.service.tests.codegen; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import io.helidon.common.types.TypeName; +import io.helidon.service.codegen.ServiceCodegenTypes; +import io.helidon.service.registry.Dependency; +import io.helidon.service.registry.DependencyContext; +import io.helidon.service.registry.GeneratedService; +import io.helidon.service.registry.Service; + +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; + +class ServiceCodegenTypesTest { + @Test + void testTypes() { + // it is really important to test ALL constants on the class, so let's use reflection + Field[] declaredFields = ServiceCodegenTypes.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 public", Modifier.isPublic(declaredField.getModifiers()), is(true)); + assertThat(name + " must be final", Modifier.isFinal(declaredField.getModifiers()), is(true)); + + toCheck.add(name); + fields.put(name, declaredField); + } + + checkField(toCheck, checked, fields, "SERVICE_ANNOTATION_PROVIDER", Service.Provider.class); + checkField(toCheck, checked, fields, "SERVICE_ANNOTATION_CONTRACT", Service.Contract.class); + checkField(toCheck, checked, fields, "SERVICE_ANNOTATION_EXTERNAL_CONTRACTS", Service.ExternalContracts.class); + checkField(toCheck, checked, fields, "SERVICE_ANNOTATION_DESCRIPTOR", Service.Descriptor.class); + checkField(toCheck, checked, fields, "SERVICE_DESCRIPTOR", GeneratedService.Descriptor.class); + checkField(toCheck, checked, fields, "SERVICE_DEPENDENCY", Dependency.class); + checkField(toCheck, checked, fields, "SERVICE_DEPENDENCY_CONTEXT", DependencyContext.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"); + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/BigHammer.java b/service/tests/codegen/src/test/java/module-info.java similarity index 58% rename from examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/BigHammer.java rename to service/tests/codegen/src/test/java/module-info.java index eab6cfaf221..49940eb0f05 100644 --- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/BigHammer.java +++ b/service/tests/codegen/src/test/java/module-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. @@ -14,17 +14,15 @@ * limitations under the License. */ -package io.helidon.examples.inject.basics; +module io.helidon.service.tests.codegen.test { + exports io.helidon.service.tests.codegen; -import jakarta.inject.Singleton; + requires io.helidon.service.registry; + requires io.helidon.service.codegen; + requires io.helidon.config.metadata; -@Big -@Singleton -class BigHammer extends Hammer { + requires hamcrest.all; + requires org.junit.jupiter.api; - @Override - public String name() { - return "Big " + super.name(); - } - -} + opens io.helidon.service.tests.codegen to org.junit.platform.commons; +} \ No newline at end of file diff --git a/service/tests/pom.xml b/service/tests/pom.xml new file mode 100644 index 00000000000..8c3134847f3 --- /dev/null +++ b/service/tests/pom.xml @@ -0,0 +1,51 @@ + + + + + io.helidon.service + helidon-service-project + 4.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + io.helidon.service.tests + helidon-service-tests-project + + Helidon Service Tests Project + pom + + + true + true + true + true + true + true + true + + + + registry + codegen + + + diff --git a/service/tests/registry/pom.xml b/service/tests/registry/pom.xml new file mode 100644 index 00000000000..3ab2a699224 --- /dev/null +++ b/service/tests/registry/pom.xml @@ -0,0 +1,110 @@ + + + + + io.helidon.service.tests + helidon-service-tests-project + 4.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + helidon-service-tests-registry + + Helidon Service Tests Registry + + + false + + + + + io.helidon.service + helidon-service-registry + + + + io.helidon.config + helidon-config + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + io.helidon.common.testing + helidon-common-testing-junit5 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + diff --git a/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/Wrench.java b/service/tests/registry/src/main/java/io/helidon/service/test/registry/MyContract.java similarity index 73% rename from examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/Wrench.java rename to service/tests/registry/src/main/java/io/helidon/service/test/registry/MyContract.java index 247e00b3477..42c5a4cb7ca 100644 --- a/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/Wrench.java +++ b/service/tests/registry/src/main/java/io/helidon/service/test/registry/MyContract.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,10 +14,11 @@ * limitations under the License. */ -package io.helidon.examples.inject.providers; +package io.helidon.service.test.registry; -import io.helidon.examples.inject.basics.Tool; - -public interface Wrench extends Tool { +import io.helidon.service.registry.Service; +@Service.Contract +public interface MyContract { + String message(); } diff --git a/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/AllenWrench.java b/service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService.java similarity index 64% rename from examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/AllenWrench.java rename to service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService.java index e218d8c3245..4e8abd86080 100644 --- a/examples/inject/providers/src/test/java/io/helidon/examples/inject/providers/AllenWrench.java +++ b/service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService.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,20 @@ * limitations under the License. */ -package io.helidon.examples.inject.providers; +package io.helidon.service.test.registry; -import jakarta.inject.Singleton; +import io.helidon.service.registry.Service; -@Singleton -@SuppressWarnings("unused") -public class AllenWrench implements Wrench { +@Service.Provider +class MyService implements MyContract { + static int instances = 0; - @Override - public String name() { - return "Allen Wrench"; + MyService() { + instances++; } + @Override + public String message() { + return "MyService"; + } } diff --git a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/StandardNail.java b/service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService2.java similarity index 55% rename from examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/StandardNail.java rename to service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService2.java index 00eb469c237..cc64d0419e1 100644 --- a/examples/inject/providers/src/main/java/io/helidon/examples/inject/providers/StandardNail.java +++ b/service/tests/registry/src/main/java/io/helidon/service/test/registry/MyService2.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,21 +14,25 @@ * limitations under the License. */ -package io.helidon.examples.inject.providers; +package io.helidon.service.test.registry; -import java.util.concurrent.atomic.AtomicInteger; +import io.helidon.common.Weight; +import io.helidon.service.registry.Service; -class StandardNail implements Nail { +@Service.Provider +@Weight(102) +class MyService2 implements MyContract { + static int instances; - private static final AtomicInteger counter = new AtomicInteger(); - private final int id = counter.incrementAndGet(); + private final MyService service; - StandardNail() { + MyService2(MyService service) { + instances++; + this.service = service; } @Override - public int id() { - return id; + public String message() { + return service.message() + ":MyService2"; } - } diff --git a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/package-info.java b/service/tests/registry/src/main/java/module-info.java similarity index 75% rename from examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/package-info.java rename to service/tests/registry/src/main/java/module-info.java index 5ad11f7d68f..88f0d5cf924 100644 --- a/examples/inject/basics/src/main/java/io/helidon/examples/inject/basics/package-info.java +++ b/service/tests/registry/src/main/java/module-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. @@ -14,7 +14,8 @@ * limitations under the License. */ -/** - * Examples of programmatic and declarative usages of Injection. - */ -package io.helidon.examples.inject.basics; +module io.helidon.service.tests.core { + requires io.helidon.service.registry; + + exports io.helidon.service.test.registry; +} \ No newline at end of file diff --git a/service/tests/registry/src/test/java/io/helidon/service/test/registry/CyclicDependencyTest.java b/service/tests/registry/src/test/java/io/helidon/service/test/registry/CyclicDependencyTest.java new file mode 100644 index 00000000000..9d0e07e1c27 --- /dev/null +++ b/service/tests/registry/src/test/java/io/helidon/service/test/registry/CyclicDependencyTest.java @@ -0,0 +1,148 @@ +/* + * 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.test.registry; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.GenericType; +import io.helidon.common.types.TypeName; +import io.helidon.service.registry.Dependency; +import io.helidon.service.registry.DependencyContext; +import io.helidon.service.registry.GeneratedService; +import io.helidon.service.registry.ServiceRegistry; +import io.helidon.service.registry.ServiceRegistryConfig; +import io.helidon.service.registry.ServiceRegistryException; +import io.helidon.service.registry.ServiceRegistryManager; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class CyclicDependencyTest { + private static final TypeName SERVICE_1 = TypeName.create(Service1.class); + private static final TypeName SERVICE_2 = TypeName.create(Service2.class); + + @Test + public void testCyclicDependency() { + ServiceRegistryManager manager = ServiceRegistryManager.create(ServiceRegistryConfig.builder() + .discoverServices(false) + .addServiceDescriptor(new Descriptor1()) + .addServiceDescriptor(new Descriptor2()) + .build()); + + try { + ServiceRegistry registry = manager.registry(); + ServiceRegistryException sre = assertThrows(ServiceRegistryException.class, () -> registry.get(Service1.class)); + assertThat(sre.getMessage(), startsWith("Cyclic dependency")); + sre = assertThrows(ServiceRegistryException.class, () -> registry.get(Service2.class)); + assertThat(sre.getMessage(), startsWith("Cyclic dependency")); + } finally { + manager.shutdown(); + } + } + + private static class Service1 { + Service1(Service2 second) { + } + } + + private static class Service2 { + Service2(Service1 first) { + } + } + + private static class Descriptor1 implements GeneratedService.Descriptor { + private static final TypeName TYPE = TypeName.create(Descriptor1.class); + + private static final Dependency DEP = Dependency.builder() + .contract(SERVICE_2) + .descriptor(TYPE) + .descriptorConstant("DEP") + .name("second") + .service(SERVICE_1) + .typeName(SERVICE_2) + .contractType(new GenericType() { }) + .build(); + + @Override + public Object instantiate(DependencyContext ctx) { + return new Service1(ctx.dependency(DEP)); + } + + @Override + public TypeName serviceType() { + return SERVICE_1; + } + + @Override + public TypeName descriptorType() { + return TYPE; + } + + @Override + public List dependencies() { + return List.of(DEP); + } + + @Override + public Set contracts() { + return Set.of(SERVICE_1); + } + } + + private static class Descriptor2 implements GeneratedService.Descriptor { + private static final TypeName TYPE = TypeName.create(Descriptor2.class); + + private static final Dependency DEP = Dependency.builder() + .contract(SERVICE_1) + .descriptor(TYPE) + .descriptorConstant("DEP") + .name("first") + .service(SERVICE_2) + .typeName(SERVICE_1) + .contractType(new GenericType() { }) + .build(); + + @Override + public Object instantiate(DependencyContext ctx) { + return new Service2(ctx.dependency(DEP)); + } + + @Override + public TypeName serviceType() { + return SERVICE_1; + } + + @Override + public TypeName descriptorType() { + return TYPE; + } + + @Override + public List dependencies() { + return List.of(DEP); + } + + @Override + public Set contracts() { + return Set.of(SERVICE_2); + } + } +} diff --git a/service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryConfigTest.java b/service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryConfigTest.java new file mode 100644 index 00000000000..c9a4eb9949c --- /dev/null +++ b/service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryConfigTest.java @@ -0,0 +1,61 @@ +/* + * 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.test.registry; + +import java.util.Map; + +import io.helidon.config.Config; +import io.helidon.config.ConfigSources; +import io.helidon.service.registry.ServiceRegistryConfig; + +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.is; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsEmptyCollection.empty; + +public class RegistryConfigTest { + @Test + void testDefaults() { + ServiceRegistryConfig cfg = ServiceRegistryConfig.create(); + assertThat(cfg.config(), is(optionalEmpty())); + assertThat(cfg.discoverServices(), is(true)); + assertThat(cfg.serviceDescriptors(), is(empty())); + assertThat(cfg.serviceInstances().size(), is(0)); + } + + @Test + void testFromConfig() { + Config config = Config.builder( + ConfigSources.create( + Map.of("registry.discover-services", "false" + ), "config-1")) + .disableEnvironmentVariablesSource() + .disableSystemPropertiesSource() + .build(); + Config registryConfig = config.get("registry"); + ServiceRegistryConfig cfg = ServiceRegistryConfig.create(registryConfig); + + assertThat(cfg.config(), optionalValue(sameInstance(registryConfig))); + assertThat(cfg.discoverServices(), is(false)); + assertThat(cfg.serviceDescriptors(), is(empty())); + assertThat(cfg.serviceInstances().size(), is(0)); + } +} diff --git a/service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryTest.java b/service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryTest.java new file mode 100644 index 00000000000..70e6aed2be3 --- /dev/null +++ b/service/tests/registry/src/test/java/io/helidon/service/test/registry/RegistryTest.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.service.test.registry; + +import java.util.List; + +import io.helidon.service.registry.ServiceRegistry; +import io.helidon.service.registry.ServiceRegistryManager; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +public class RegistryTest { + private static ServiceRegistryManager registryManager; + private static ServiceRegistry registry; + + @BeforeAll + public static void init() { + registryManager = ServiceRegistryManager.create(); + registry = registryManager.registry(); + } + + @AfterAll + public static void shutdown() { + if (registryManager != null) { + registryManager.shutdown(); + } + registryManager = null; + registry = null; + } + + @Test + public void testRegistryGet() { + MyContract myContract = registry.get(MyContract.class); + // higher weight + assertThat(myContract, instanceOf(MyService2.class)); + + assertThat(MyService2.instances, is(1)); + assertThat(MyService.instances, is(1)); + } + + @Test + public void testRegistryFirst() { + MyContract myContract = registry.first(MyContract.class).get(); + // higher weight + assertThat(myContract, instanceOf(MyService2.class)); + assertThat(MyService2.instances, is(1)); + assertThat(MyService.instances, is(1)); + } + + @Test + public void testRegistryAll() { + List myContracts = registry.all(MyContract.class); + assertThat(myContracts, hasSize(2)); + // higher weight + assertThat(myContracts.get(0), instanceOf(MyService2.class)); + assertThat(myContracts.get(1), instanceOf(MyService.class)); + + assertThat(MyService2.instances, is(1)); + assertThat(MyService.instances, is(1)); + } +} diff --git a/tests/integration/native-image/mp-1/pom.xml b/tests/integration/native-image/mp-1/pom.xml index 43e5e2dc21c..e6265672e8a 100644 --- a/tests/integration/native-image/mp-1/pom.xml +++ b/tests/integration/native-image/mp-1/pom.xml @@ -53,11 +53,6 @@ io.helidon.security.providers helidon-security-providers-oidc - - io.helidon.inject - helidon-inject-runtime - true - io.helidon.tracing.providers helidon-tracing-providers-jaeger diff --git a/tests/integration/native-image/mp-1/src/main/java/module-info.java b/tests/integration/native-image/mp-1/src/main/java/module-info.java index 4c64bf47b48..d99ace56449 100644 --- a/tests/integration/native-image/mp-1/src/main/java/module-info.java +++ b/tests/integration/native-image/mp-1/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. @@ -40,9 +40,6 @@ // never reach health check CDI extension requires io.helidon.health.checks; - // needed to compile injection generated classes - requires static io.helidon.inject.runtime; - exports io.helidon.tests.integration.nativeimage.mp1; exports io.helidon.tests.integration.nativeimage.mp1.other; 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-mp-1/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 index 695c051e404..f53634fa17d 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-mp-1/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 @@ -14,4 +14,7 @@ # limitations under the License. # -Args=--initialize-at-build-time=io.helidon.tests.integration.nativeimage.mp1 +Args=--initialize-at-build-time=io.helidon.tests.integration.nativeimage.mp1 \ + --initialize-at-build-time=io.helidon.common.features \ + --initialize-at-build-time=io.helidon.jersey.client.JerseyClientBuilderListener \ + --initialize-at-build-time=io.helidon.logging.jul.JulMdcPropagator diff --git a/tests/integration/native-image/mp-3/pom.xml b/tests/integration/native-image/mp-3/pom.xml index f54cc2d5515..99adea0f478 100644 --- a/tests/integration/native-image/mp-3/pom.xml +++ b/tests/integration/native-image/mp-3/pom.xml @@ -42,11 +42,6 @@ io.helidon.microprofile.bundles helidon-microprofile - - io.helidon.inject - helidon-inject-runtime - true - io.smallrye jandex diff --git a/tests/integration/vault/pom.xml b/tests/integration/vault/pom.xml index 677424ab280..a9065651271 100644 --- a/tests/integration/vault/pom.xml +++ b/tests/integration/vault/pom.xml @@ -113,5 +113,10 @@ helidon-common-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + \ No newline at end of file diff --git a/webclient/api/pom.xml b/webclient/api/pom.xml index 6cf10fed096..f4083b8d2a7 100644 --- a/webclient/api/pom.xml +++ b/webclient/api/pom.xml @@ -77,16 +77,6 @@ io.helidon.builder helidon-builder-api - - io.helidon.inject.configdriven - helidon-inject-configdriven-api - true - - - io.helidon.inject.configdriven - helidon-inject-configdriven-runtime - true - org.junit.jupiter junit-jupiter-api @@ -136,12 +126,6 @@ helidon-codegen-helidon-copyright ${helidon.version} - - - io.helidon.inject.configdriven - helidon-inject-configdriven-processor - ${helidon.version} - io.helidon.config helidon-config-metadata-processor @@ -150,11 +134,6 @@ - - io.helidon.inject.configdriven - helidon-inject-configdriven-processor - ${helidon.version} - io.helidon.common.features helidon-common-features-processor diff --git a/webclient/api/src/main/java/io/helidon/webclient/api/LoomClient.java b/webclient/api/src/main/java/io/helidon/webclient/api/LoomClient.java index 8039bb12c46..616ae4918ba 100644 --- a/webclient/api/src/main/java/io/helidon/webclient/api/LoomClient.java +++ b/webclient/api/src/main/java/io/helidon/webclient/api/LoomClient.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. @@ -28,20 +28,16 @@ import io.helidon.common.HelidonServiceLoader; import io.helidon.common.LazyValue; import io.helidon.http.Method; -import io.helidon.inject.configdriven.api.ConfigDriven; import io.helidon.webclient.spi.ClientProtocolProvider; import io.helidon.webclient.spi.HttpClientSpi; import io.helidon.webclient.spi.HttpClientSpiProvider; import io.helidon.webclient.spi.Protocol; import io.helidon.webclient.spi.ProtocolConfig; -import jakarta.inject.Inject; - /** * Base class for HTTP implementations of {@link WebClient}. */ @SuppressWarnings("rawtypes") -@ConfigDriven(WebClientConfigBlueprint.class) class LoomClient implements WebClient { static final LazyValue EXECUTOR = LazyValue.create(() -> { return Executors.newThreadPerTaskExecutor(Thread.ofVirtual() @@ -75,7 +71,6 @@ class LoomClient implements WebClient { * @param config builder the subclass is built from */ @SuppressWarnings({"rawtypes", "unchecked"}) - @Inject protected LoomClient(WebClientConfig config) { this.config = config; this.protocolConfigs = ProtocolConfigs.create(config.protocolConfigs()); diff --git a/webclient/api/src/main/java/io/helidon/webclient/api/WebClientConfigBlueprint.java b/webclient/api/src/main/java/io/helidon/webclient/api/WebClientConfigBlueprint.java index f2019e26532..aec75406815 100644 --- a/webclient/api/src/main/java/io/helidon/webclient/api/WebClientConfigBlueprint.java +++ b/webclient/api/src/main/java/io/helidon/webclient/api/WebClientConfigBlueprint.java @@ -20,14 +20,12 @@ import io.helidon.builder.api.Option; import io.helidon.builder.api.Prototype; -import io.helidon.inject.configdriven.api.ConfigBean; import io.helidon.webclient.spi.ProtocolConfig; import io.helidon.webclient.spi.ProtocolConfigProvider; /** * WebClient configuration. */ -@ConfigBean(repeatable = true, wantDefault = true) @Prototype.Blueprint @Prototype.Configured("clients") interface WebClientConfigBlueprint extends HttpClientConfigBlueprint, Prototype.Factory { diff --git a/webclient/api/src/main/java/module-info.java b/webclient/api/src/main/java/module-info.java index 9962fc5e408..70636be3d5a 100644 --- a/webclient/api/src/main/java/module-info.java +++ b/webclient/api/src/main/java/module-info.java @@ -31,9 +31,6 @@ requires static io.helidon.common.features.api; // @Feature requires static io.helidon.config.metadata; // @ConfiguredOption etc - requires static io.helidon.inject.configdriven.api; - requires static io.helidon.inject.configdriven.runtime; - requires static jakarta.inject; // Injection support requires transitive io.helidon.common.config; requires transitive io.helidon.common.configurable; diff --git a/webclient/http1/pom.xml b/webclient/http1/pom.xml index 5c20441e312..bae4376e42a 100644 --- a/webclient/http1/pom.xml +++ b/webclient/http1/pom.xml @@ -77,16 +77,6 @@ io.helidon.builder helidon-builder-api - - io.helidon.inject.configdriven - helidon-inject-configdriven-api - true - - - io.helidon.inject.configdriven - helidon-inject-configdriven-runtime - true - io.helidon.logging helidon-logging-jul @@ -126,11 +116,6 @@ helidon-common-features-processor ${helidon.version} - - io.helidon.inject.configdriven - helidon-inject-configdriven-processor - ${helidon.version} - io.helidon.config helidon-config-metadata-processor @@ -154,11 +139,6 @@ - - io.helidon.inject.configdriven - helidon-inject-configdriven-processor - ${helidon.version} - io.helidon.common.features helidon-common-features-processor diff --git a/webserver/webserver/pom.xml b/webserver/webserver/pom.xml index 19181012a4a..f9915e2315a 100644 --- a/webserver/webserver/pom.xml +++ b/webserver/webserver/pom.xml @@ -135,11 +135,6 @@ io.helidon.common.testing test - - io.helidon.inject - helidon-inject-testing - test - diff --git a/webserver/webserver/src/test/java/io/helidon/webserver/WebServerConfigDrivenTest.java b/webserver/webserver/src/test/java/io/helidon/webserver/WebServerConfigDrivenTest.java deleted file mode 100644 index 5ae961cc649..00000000000 --- a/webserver/webserver/src/test/java/io/helidon/webserver/WebServerConfigDrivenTest.java +++ /dev/null @@ -1,69 +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.webserver; - -import io.helidon.config.Config; -import io.helidon.inject.api.Bootstrap; -import io.helidon.inject.api.InjectionServices; -import io.helidon.inject.api.Phase; -import io.helidon.inject.api.ServiceProvider; -import io.helidon.inject.api.Services; -import io.helidon.inject.testing.InjectionTestingSupport; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -@Disabled -class WebServerConfigDrivenTest { - static final boolean NORMAL_PRODUCTION_PATH = false; - - @AfterEach - public void reset() { - if (!NORMAL_PRODUCTION_PATH) { - // requires 'inject.permits-dynamic=true' to be able to reset - InjectionTestingSupport.resetAll(); - } - } - - @Test - void testConfigDriven() { - // This will pick up application.yaml from the classpath as default configuration file - Config config = Config.create(); - - if (NORMAL_PRODUCTION_PATH) { - // bootstrap Injection with our config tree when it initializes - InjectionServices.globalBootstrap(Bootstrap.builder().config(config).build()); - } - - // initialize Injection, and drive all activations based upon what has been configured - Services services; - if (NORMAL_PRODUCTION_PATH) { - services = InjectionServices.realizedServices(); - } else { - InjectionServices injectionServices = InjectionTestingSupport.testableServices(config); - services = injectionServices.services(); - } - - ServiceProvider webServerSp = services.lookupFirst(WebServer.class); - assertThat(webServerSp.currentActivationPhase(), is(Phase.ACTIVE)); - } - -} From cf821988c2035e0c6d443f8d658dd9781b2bdd66 Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Wed, 29 May 2024 16:01:05 +0200 Subject: [PATCH 029/245] 4.x: Use System.Logger instead of JUL where applicable #7792 (#8791) * 4.x: Use System.Logger instead of JUL where applicable #7792 Signed-off-by: Jorge Bescos Gascon --- common/common/src/main/java/module-info.java | 5 +-- common/configurable/pom.xml | 12 ++++++ .../src/main/java/module-info.java | 2 +- .../common/configurable/ThreadPoolTest.java | 2 +- .../helidon/config/etcd/EtcdConfigSource.java | 8 ++-- .../io/helidon/config/etcd/EtcdWatcher.java | 12 +++--- .../etcd/internal/client/v2/EtcdV2Client.java | 8 ++-- .../etcd/internal/client/v3/EtcdV3Client.java | 8 ++-- config/etcd/src/main/java/module-info.java | 3 +- config/hocon/src/main/java/module-info.java | 3 +- config/yaml/src/main/java/module-info.java | 3 +- .../main/java/io/helidon/cors/Aggregator.java | 3 +- .../io/helidon/cors/CorsSupportHelper.java | 6 +-- .../main/java/io/helidon/cors/LogHelper.java | 10 ++--- cors/src/main/java/module-info.java | 3 +- .../dbclient/src/main/java/module-info.java | 3 +- dbclient/jsonp/src/main/java/module-info.java | 4 +- .../integrations/common/rest/RestApiBase.java | 38 +++++++++--------- .../rest/src/main/java/module-info.java | 3 +- .../micronaut/cdi/MicronautCdiExtension.java | 10 ++--- .../micronaut/cdi/MicronautInterceptor.java | 7 ++-- .../cdi/MicronautMethodInvocationContext.java | 9 ++--- .../cdi/src/main/java/module-info.java | 3 +- .../neo4j/src/main/java/module-info.java | 3 +- .../oci/metrics/OciMetricsSupport.java | 25 ++++++------ .../metrics/src/main/java/module-info.java | 1 - .../vault/cdi/src/main/java/module-info.java | 3 +- .../cubbyhole/src/main/java/module-info.java | 3 +- .../database/src/main/java/module-info.java | 4 +- .../kv1/src/main/java/module-info.java | 3 +- .../kv2/src/main/java/module-info.java | 3 +- .../pki/src/main/java/module-info.java | 3 +- .../sys/sys/src/main/java/module-info.java | 3 +- jersey/common/src/main/java/module-info.java | 3 +- .../jersey/connector/HelidonConnector.java | 7 ++-- .../jersey/connector/ProxyBuilder.java | 5 ++- .../connector/src/main/java/module-info.java | 3 +- .../aq/src/main/java/module-info.java | 3 +- .../mock/src/main/java/module-info.java | 3 +- .../wls-jms/src/main/java/module-info.java | 3 +- .../cdi/src/main/java/module-info.java | 1 - .../messaging/IncomingMethod.java | 34 ++++++++-------- .../microprofile/messaging/LatestEmitter.java | 10 ++--- .../messaging/ProcessorMethod.java | 8 ++-- .../messaging/ProxySubscriber.java | 9 ++--- .../messaging/UniversalChannel.java | 9 ++--- .../core/src/main/java/module-info.java | 3 +- .../health/src/main/java/module-info.java | 3 +- .../metrics/src/main/java/module-info.java | 3 +- .../oidc/src/main/java/module-info.java | 3 +- .../src/main/java/module-info.java | 1 - .../telemetry/src/main/java/module-info.java | 1 - .../common/src/main/java/module-info.java | 3 +- .../security/DefaultAuditProvider.java | 40 +++++++++---------- .../io/helidon/security/ProviderRequest.java | 15 +++---- .../io/helidon/security/SecurityImpl.java | 8 ++-- .../io/helidon/security/SecurityUtil.java | 15 +++---- .../helidon/security/spi/AuditProvider.java | 8 ++-- .../security/src/main/java/module-info.java | 3 +- .../src/main/java/module-info.java | 3 +- .../src/main/java/module-info.java | 3 +- .../mp-3/src/main/java/module-info.java | 5 +-- .../src/main/java/module-info.java | 3 +- .../zipkin/src/main/java/module-info.java | 1 - .../graphql/src/main/java/module-info.java | 3 +- webserver/grpc/src/main/java/module-info.java | 1 - .../log/src/main/java/module-info.java | 3 +- .../webserver/security/SecurityHandler.java | 12 +++--- .../security/SecurityHttpFeature.java | 5 +-- .../security/src/main/java/module-info.java | 3 +- 70 files changed, 200 insertions(+), 262 deletions(-) diff --git a/common/common/src/main/java/module-info.java b/common/common/src/main/java/module-info.java index 8fe413c98fb..1d5aa9bc24a 100644 --- a/common/common/src/main/java/module-info.java +++ b/common/common/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. @@ -19,9 +19,6 @@ */ module io.helidon.common { - // used only by LogConfig - requires java.logging; - exports io.helidon.common; } diff --git a/common/configurable/pom.xml b/common/configurable/pom.xml index 23c41caea53..bbdfe29a263 100644 --- a/common/configurable/pom.xml +++ b/common/configurable/pom.xml @@ -116,6 +116,18 @@ + + + default-testCompile + + + + --add-modules + java.logging + + + + io.helidon.config diff --git a/common/configurable/src/main/java/module-info.java b/common/configurable/src/main/java/module-info.java index d1e4a92b150..45acfd432d2 100644 --- a/common/configurable/src/main/java/module-info.java +++ b/common/configurable/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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/common/configurable/src/test/java/io/helidon/common/configurable/ThreadPoolTest.java b/common/configurable/src/test/java/io/helidon/common/configurable/ThreadPoolTest.java index f1402be5bd3..49a256eb84f 100644 --- a/common/configurable/src/test/java/io/helidon/common/configurable/ThreadPoolTest.java +++ b/common/configurable/src/test/java/io/helidon/common/configurable/ThreadPoolTest.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. diff --git a/config/etcd/src/main/java/io/helidon/config/etcd/EtcdConfigSource.java b/config/etcd/src/main/java/io/helidon/config/etcd/EtcdConfigSource.java index a29c42131c0..281a51bfaad 100644 --- a/config/etcd/src/main/java/io/helidon/config/etcd/EtcdConfigSource.java +++ b/config/etcd/src/main/java/io/helidon/config/etcd/EtcdConfigSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 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. @@ -22,8 +22,6 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.common.media.type.MediaType; import io.helidon.common.media.type.MediaTypes; @@ -51,7 +49,7 @@ public class EtcdConfigSource extends AbstractConfigSource implements PollableSource, WatchableSource, ParsableSource { - private static final Logger LOGGER = Logger.getLogger(EtcdConfigSource.class.getName()); + private static final System.Logger LOGGER = System.getLogger(EtcdConfigSource.class.getName()); private final EtcdEndpoint endpoint; private final List endpoints; @@ -119,7 +117,7 @@ public Optional load() throws ConfigException { try { content = etcdClient().get(endpoint.key()); } catch (EtcdClientException e) { - LOGGER.log(Level.FINEST, "Get operation threw an exception.", e); + LOGGER.log(System.Logger.Level.TRACE, "Get operation threw an exception.", e); throw new ConfigException(String.format("Could not get data for key '%s'", endpoint.key()), e); } diff --git a/config/etcd/src/main/java/io/helidon/config/etcd/EtcdWatcher.java b/config/etcd/src/main/java/io/helidon/config/etcd/EtcdWatcher.java index b642ff46a46..f9fe4021b52 100644 --- a/config/etcd/src/main/java/io/helidon/config/etcd/EtcdWatcher.java +++ b/config/etcd/src/main/java/io/helidon/config/etcd/EtcdWatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 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. @@ -19,8 +19,6 @@ import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.config.Config; import io.helidon.config.etcd.EtcdConfigSourceBuilder.EtcdEndpoint; @@ -34,7 +32,7 @@ */ public class EtcdWatcher implements ChangeWatcher { - private static final Logger LOGGER = Logger.getLogger(EtcdWatcher.class.getName()); + private static final System.Logger LOGGER = System.getLogger(EtcdWatcher.class.getName()); private final AtomicBoolean started = new AtomicBoolean(); @@ -80,7 +78,7 @@ public void start(EtcdEndpoint endpoint, Consumer> lis Flow.Publisher watchPublisher = etcdClient().watch(endpoint.key()); watchPublisher.subscribe(new EtcdWatchSubscriber(listener, endpoint)); } catch (EtcdClientException ex) { - LOGGER.log(Level.WARNING, String.format("Subscription on watching on '%s' key has failed. " + LOGGER.log(System.Logger.Level.WARNING, String.format("Subscription on watching on '%s' key has failed. " + "Watching by '%s' polling strategy will not start.", EtcdWatcher.this.endpoint.key(), EtcdWatcher.this), ex); @@ -96,7 +94,7 @@ public void stop() { try { this.etcdClient.close(); } catch (EtcdClientException e) { - LOGGER.log(Level.FINE, "Faield to close etcd client", e); + LOGGER.log(System.Logger.Level.TRACE, "Faield to close etcd client", e); } } @@ -142,7 +140,7 @@ public void onNext(Long item) { @Override public void onError(Throwable throwable) { - LOGGER.log(Level.WARNING, + LOGGER.log(System.Logger.Level.WARNING, String.format( "Watching on '%s' key has failed. Watching will not continue. ", endpoint.key()), diff --git a/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v2/EtcdV2Client.java b/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v2/EtcdV2Client.java index 4f0a563dc00..a56f474cd09 100644 --- a/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v2/EtcdV2Client.java +++ b/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v2/EtcdV2Client.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. @@ -25,8 +25,6 @@ import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.config.etcd.internal.client.EtcdClient; import io.helidon.config.etcd.internal.client.EtcdClientException; @@ -45,7 +43,7 @@ public class EtcdV2Client implements EtcdClient { - private static final Logger LOGGER = Logger.getLogger(EtcdV2Client.class.getName()); + private static final System.Logger LOGGER = System.getLogger(EtcdV2Client.class.getName()); private final Map> publishers = new ConcurrentHashMap<>(); private final mousio.etcd4j.EtcdClient etcd; @@ -150,7 +148,7 @@ public void onResponse(ResponsePromise responsePromise) { publisher.submit(modifiedIndex); waitForChange(modifiedIndex + 1); } catch (Exception e) { - LOGGER.log(Level.CONFIG, "Cannot read changed value.", e); + LOGGER.log(System.Logger.Level.INFO, "Cannot read changed value.", e); } } diff --git a/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v3/EtcdV3Client.java b/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v3/EtcdV3Client.java index f9923af0609..87ad55feeac 100644 --- a/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v3/EtcdV3Client.java +++ b/config/etcd/src/main/java/io/helidon/config/etcd/internal/client/v3/EtcdV3Client.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021 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,8 +23,6 @@ import java.util.concurrent.Flow; import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.config.etcd.internal.client.EtcdClient; import io.helidon.config.etcd.internal.client.EtcdClientException; @@ -48,7 +46,7 @@ */ public class EtcdV3Client implements EtcdClient { - private static final Logger LOGGER = Logger.getLogger(EtcdV3Client.class.getName()); + private static final System.Logger LOGGER = System.getLogger(EtcdV3Client.class.getName()); private final Map> publishers = new ConcurrentHashMap<>(); @@ -148,7 +146,7 @@ public void close() throws EtcdClientException { try { channel.awaitTermination(1, TimeUnit.SECONDS); } catch (InterruptedException e) { - LOGGER.log(Level.CONFIG, "Error closing gRPC channel, reason: " + e.getLocalizedMessage(), e); + LOGGER.log(System.Logger.Level.INFO, "Error closing gRPC channel, reason: " + e.getLocalizedMessage(), e); } finally { channel.shutdown(); } diff --git a/config/etcd/src/main/java/module-info.java b/config/etcd/src/main/java/module-info.java index 5df7ecc5899..3bb68a6a73e 100644 --- a/config/etcd/src/main/java/module-info.java +++ b/config/etcd/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. @@ -29,7 +29,6 @@ requires io.grpc.stub; requires io.helidon.common.media.type; requires io.helidon.common; - requires java.logging; requires static java.annotation; diff --git a/config/hocon/src/main/java/module-info.java b/config/hocon/src/main/java/module-info.java index 147479b2347..62a8aadd27d 100644 --- a/config/hocon/src/main/java/module-info.java +++ b/config/hocon/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. @@ -29,7 +29,6 @@ module io.helidon.config.hocon { requires io.helidon.common; - requires java.logging; requires typesafe.config; requires static io.helidon.common.features.api; diff --git a/config/yaml/src/main/java/module-info.java b/config/yaml/src/main/java/module-info.java index f4189cb3adc..9ce115a3c50 100644 --- a/config/yaml/src/main/java/module-info.java +++ b/config/yaml/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. @@ -28,7 +28,6 @@ module io.helidon.config.yaml { requires io.helidon.common; - requires java.logging; requires org.yaml.snakeyaml; requires static io.helidon.common.features.api; diff --git a/cors/src/main/java/io/helidon/cors/Aggregator.java b/cors/src/main/java/io/helidon/cors/Aggregator.java index 64f8b5b96a1..f3d440fb4b2 100644 --- a/cors/src/main/java/io/helidon/cors/Aggregator.java +++ b/cors/src/main/java/io/helidon/cors/Aggregator.java @@ -20,7 +20,6 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; -import java.util.logging.Logger; import io.helidon.common.config.Config; import io.helidon.common.config.ConfigValue; @@ -53,7 +52,7 @@ public class Aggregator { */ public static final String PATHLESS_KEY = "{+}"; - private static final Logger LOGGER = Logger.getLogger(Aggregator.class.getName()); + private static final System.Logger LOGGER = System.getLogger(Aggregator.class.getName()); // Records paths and configs added via addCrossOriginConfig private final List crossOriginConfigMatchables = new ArrayList<>(); diff --git a/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java b/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java index ed4c9207f26..51d6b753ad1 100644 --- a/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java +++ b/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java @@ -26,7 +26,6 @@ import java.util.StringTokenizer; import java.util.function.BiFunction; import java.util.function.Supplier; -import java.util.logging.Logger; import io.helidon.common.config.Config; import io.helidon.common.uri.UriInfo; @@ -59,7 +58,7 @@ public class CorsSupportHelper { static final String METHOD_NOT_IN_ALLOWED_LIST = "CORS method is not in allowed list"; static final String HEADERS_NOT_IN_ALLOWED_LIST = "CORS headers not in allowed list"; - static final Logger LOGGER = Logger.getLogger(CorsSupportHelper.class.getName()); + static final System.Logger LOGGER = System.getLogger(CorsSupportHelper.class.getName()); static final String OPAQUE_ORIGIN = "null"; // browsers might send this as Origin header if origin info is untrusted @@ -240,7 +239,8 @@ public CorsSupportHelper build() { CorsSupportHelper result = new CorsSupportHelper<>(this); - LOGGER.config(() -> String.format("CorsSupportHelper configured as: %s", result.toString())); + LOGGER.log(System.Logger.Level.INFO, + () -> String.format("CorsSupportHelper configured as: %s", result.toString())); return result; } diff --git a/cors/src/main/java/io/helidon/cors/LogHelper.java b/cors/src/main/java/io/helidon/cors/LogHelper.java index 3b121a83874..7b6a2dc72a7 100644 --- a/cors/src/main/java/io/helidon/cors/LogHelper.java +++ b/cors/src/main/java/io/helidon/cors/LogHelper.java @@ -25,8 +25,6 @@ import java.util.StringJoiner; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import io.helidon.cors.CorsSupportHelper.RequestType; @@ -36,8 +34,8 @@ class LogHelper { - static final Level DECISION_LEVEL = Level.FINE; - static final Level DETAILED_DECISION_LEVEL = Level.FINER; + static final System.Logger.Level DECISION_LEVEL = System.Logger.Level.TRACE; + static final System.Logger.Level DETAILED_DECISION_LEVEL = System.Logger.Level.TRACE; private LogHelper() { } @@ -177,11 +175,11 @@ static void logInferRequestType(RequestType result, static class MatcherChecks { private final Map checks; - private final Logger logger; + private final System.Logger logger; private final boolean isLoggable; private final Function getter; - MatcherChecks(Logger logger, Function getter) { + MatcherChecks(System.Logger logger, Function getter) { this.logger = logger; isLoggable = logger.isLoggable(DETAILED_DECISION_LEVEL); this.getter = getter; diff --git a/cors/src/main/java/module-info.java b/cors/src/main/java/module-info.java index 7250dc96191..73fbaeb496f 100644 --- a/cors/src/main/java/module-info.java +++ b/cors/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. @@ -19,7 +19,6 @@ */ module io.helidon.cors { - requires java.logging; requires io.helidon.http; requires io.helidon.common.config; diff --git a/dbclient/dbclient/src/main/java/module-info.java b/dbclient/dbclient/src/main/java/module-info.java index ea1963549b8..2353ba309ae 100644 --- a/dbclient/dbclient/src/main/java/module-info.java +++ b/dbclient/dbclient/src/main/java/module-info.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. @@ -31,7 +31,6 @@ ) module io.helidon.dbclient { - requires java.logging; requires java.sql; requires static io.helidon.common.features.api; diff --git a/dbclient/jsonp/src/main/java/module-info.java b/dbclient/jsonp/src/main/java/module-info.java index 771f3f1fc34..797ca0ce5e1 100644 --- a/dbclient/jsonp/src/main/java/module-info.java +++ b/dbclient/jsonp/src/main/java/module-info.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. @@ -29,7 +29,7 @@ requires io.helidon.dbclient; requires jakarta.json; - requires java.logging; + requires static io.helidon.common.features.api; exports io.helidon.dbclient.jsonp; diff --git a/integrations/common/rest/src/main/java/io/helidon/integrations/common/rest/RestApiBase.java b/integrations/common/rest/src/main/java/io/helidon/integrations/common/rest/RestApiBase.java index 89e8856cda6..a7ce2651f29 100644 --- a/integrations/common/rest/src/main/java/io/helidon/integrations/common/rest/RestApiBase.java +++ b/integrations/common/rest/src/main/java/io/helidon/integrations/common/rest/RestApiBase.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. @@ -19,13 +19,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; +import java.lang.System.Logger; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; -import java.util.logging.Logger; import io.helidon.common.context.Contexts; import io.helidon.common.media.type.MediaType; @@ -51,7 +51,7 @@ * such as security, processing of headers etc. */ public abstract class RestApiBase implements RestApi { - private static final Logger LOGGER = Logger.getLogger(RestApiBase.class.getName()); + private static final System.Logger LOGGER = System.getLogger(RestApiBase.class.getName()); private final WebClient webClient; private final FtHandler ftHandler; private final JsonBuilderFactory jsonBuilderFactory; @@ -78,7 +78,7 @@ public T invoke(Method method, ApiResponse.Builder responseBuilder) { String requestId = requestId(request); - LOGGER.finest(() -> requestId + ": Invoking " + method + " on path " + path + " no entity expected."); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": Invoking " + method + " on path " + path + " no entity expected."); HttpClientResponse response = ftHandler.invoke(responseSupplier(method, path, request, requestId)); return handleResponse(path, request, method, requestId, response, responseBuilder); @@ -91,7 +91,7 @@ public T invokeWithResponse(Method method, ApiEntityResponse.Builder responseBuilder) { String requestId = requestId(request); - LOGGER.finest(() -> requestId + ": Invoking " + method + " on path " + path + " JSON entity expected."); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": Invoking " + method + " on path " + path + " JSON entity expected."); Supplier responseSupplier = responseSupplier(method, path, request, requestId); @@ -108,7 +108,7 @@ public T invokeBytesRequest(Method method, String requestId = requestId(apiRequest); - LOGGER.finest(() -> requestId + ": Invoking " + method + " on path " + path + " with bytes request"); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": Invoking " + method + " on path " + path + " with bytes request"); HttpClientRequest request = webClient.method(method).path(path); addHeaders(request, apiRequest.headers()); @@ -134,7 +134,8 @@ public > T invokeEntityResponse(Method metho String requestId = requestId(request); - LOGGER.finest(() -> requestId + ": Invoking " + method + " on path " + path + " with publisher response"); + LOGGER.log(Logger.Level.TRACE, + () -> requestId + ": Invoking " + method + " on path " + path + " with publisher response"); request.responseMediaType(request.responseMediaType().orElse(MediaTypes.WILDCARD)); HttpClientResponse response = ftHandler.invoke(responseSupplier(method, path, request, requestId)); @@ -149,7 +150,7 @@ public > T invokeBytesResponse(Method method String requestId = requestId(request); - LOGGER.finest(() -> requestId + ": Invoking " + method + " on path " + path + " with bytes response"); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": Invoking " + method + " on path " + path + " with bytes response"); request.responseMediaType(request.responseMediaType().orElse(MediaTypes.WILDCARD)); @@ -166,7 +167,7 @@ public > T invokeOptional(Method method, String requestId = requestId(request); - LOGGER.finest(() -> requestId + ": Invoking " + method + " on path " + path + " with optional response"); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": Invoking " + method + " on path " + path + " with optional response"); HttpClientResponse response = ftHandler.invoke(responseSupplier(method, path, request, requestId)); return handleOptionalJsonResponse(path, request, method, requestId, response, responseBuilder); @@ -401,7 +402,8 @@ protected > T handleOptionalJsonResponse( ResponseState statusKind = responseState(path, request, method, requestId, response); if (statusKind.success) { if (statusKind.entityExpected) { - LOGGER.finest(() -> requestId + ": " + method + " on path " + path + " returned " + response.status()); + LOGGER.log(Logger.Level.TRACE, + () -> requestId + ": " + method + " on path " + path + " returned " + response.status()); if (response.headers().contentLength().orElse(-1L) == 0) { // explicit content length set to 0 return emptyResponse(path, request, method, requestId, response, responseBuilder); @@ -416,7 +418,7 @@ protected > T handleOptionalJsonResponse( } return emptyResponse(path, request, method, requestId, response, responseBuilder); } - LOGGER.finest(() -> requestId + ": " + method + " on path " + path + " failed " + response.status()); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": " + method + " on path " + path + " failed " + response.status()); throw responseError(path, request, method, requestId, response); } @@ -498,7 +500,7 @@ protected T handleJsonResponse( Status status = response.status(); if (Status.Family.of(status.code()) == Status.Family.SUCCESSFUL) { - LOGGER.finest(() -> requestId + ": " + method + " on path " + path + " returned " + status); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": " + method + " on path " + path + " returned " + status); try { JsonObject entity = response.entity().as(JsonObject.class); return jsonOkResponse(path, request, method, requestId, response, entity, responseBuilder); @@ -506,7 +508,7 @@ protected T handleJsonResponse( throw readErrorFailedEntity(path, request, method, requestId, response, ex); } } - LOGGER.finest(() -> requestId + ": " + method + " on path " + path + " failed " + status); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": " + method + " on path " + path + " failed " + status); throw responseError(path, request, method, requestId, response); } @@ -534,10 +536,10 @@ protected T handleResponse(String path, boolean success = (Status.Family.of(status.code()) == Status.Family.SUCCESSFUL); if (success) { - LOGGER.finest(() -> requestId + ": " + method + " on path " + path + " returned " + status); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": " + method + " on path " + path + " returned " + status); return noEntityOkResponse(path, request, method, requestId, response, responseBuilder); } - LOGGER.finest(() -> requestId + ": " + method + " on path " + path + " failed " + status); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": " + method + " on path " + path + " failed " + status); throw responseError(path, request, method, requestId, response); } @@ -622,7 +624,7 @@ protected ApiRestException readError(String path, String requestId, HttpClientResponse response, String entity) { - LOGGER.finest(() -> requestId + ": request failed for path " + path + ", error response: " + entity); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": request failed for path " + path + ", error response: " + entity); return RestException.builder() .requestId(requestId) @@ -648,7 +650,7 @@ protected ApiRestException readError(String path, Method method, String requestId, HttpClientResponse response) { - LOGGER.finest(() -> requestId + ": request failed for path " + path); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": request failed for path " + path); return RestException.builder() .requestId(requestId) @@ -676,7 +678,7 @@ protected ApiRestException readError(String path, String requestId, HttpClientResponse response, JsonObject errorObject) { - LOGGER.finest(() -> requestId + ": request failed for path " + path + ", error object: " + errorObject); + LOGGER.log(Logger.Level.TRACE, () -> requestId + ": request failed for path " + path + ", error object: " + errorObject); return RestException.builder() .requestId(requestId) .status(response.status()) diff --git a/integrations/common/rest/src/main/java/module-info.java b/integrations/common/rest/src/main/java/module-info.java index 392fe2eff69..e67e111f053 100644 --- a/integrations/common/rest/src/main/java/module-info.java +++ b/integrations/common/rest/src/main/java/module-info.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. @@ -31,7 +31,6 @@ requires io.helidon.http.media.jsonp; requires io.helidon.tracing; requires io.helidon.webclient; - requires java.logging; requires transitive io.helidon.common; requires transitive jakarta.json; diff --git a/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautCdiExtension.java b/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautCdiExtension.java index 9b234c3b6a7..b0979452eb6 100644 --- a/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautCdiExtension.java +++ b/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautCdiExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022 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. @@ -28,8 +28,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -69,7 +67,7 @@ * scope), and adds support for invoking Micronaut interceptors. */ public class MicronautCdiExtension implements Extension { - private static final Logger LOGGER = Logger.getLogger(MicronautCdiExtension.class.getName()); + private static final System.Logger LOGGER = System.getLogger(MicronautCdiExtension.class.getName()); private static final String MICRONAUT_BEAN_PREFIX = "micronaut-"; private final AtomicReference micronautContext = new AtomicReference<>(); @@ -340,8 +338,8 @@ private Stream loadMicronautService(ServiceDefinition next = remaining.next(); - LOGGER.finest(() -> "Micronaut interceptor: " + next.getClass().getName()); + LOGGER.log(System.Logger.Level.TRACE, () -> "Micronaut interceptor: " + next.getClass().getName()); return next.intercept(context); } } diff --git a/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautMethodInvocationContext.java b/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautMethodInvocationContext.java index 15c39f99ffc..6d32c1645c7 100644 --- a/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautMethodInvocationContext.java +++ b/integrations/micronaut/cdi/src/main/java/io/helidon/integrations/micronaut/cdi/MicronautMethodInvocationContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 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. @@ -21,7 +21,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import java.util.logging.Logger; import io.helidon.common.LazyValue; @@ -43,7 +42,7 @@ */ @SuppressWarnings({"rawtypes", "unchecked"}) class MicronautMethodInvocationContext implements MethodInvocationContext { - private static final Logger LOGGER = Logger.getLogger(MicronautMethodInvocationContext.class.getName()); + private static final System.Logger LOGGER = System.getLogger(MicronautMethodInvocationContext.class.getName()); private final InvocationContext cdiContext; private final ExecutableMethod executableMethod; @@ -107,7 +106,7 @@ public Object getTarget() { public Object proceed() throws RuntimeException { if (remaining.hasNext()) { MethodInterceptor next = remaining.next(); - LOGGER.finest(() -> "Micronaut interceptor: " + next.getClass().getName()); + LOGGER.log(System.Logger.Level.TRACE, () -> "Micronaut interceptor: " + next.getClass().getName()); return next.intercept(this); } try { @@ -120,7 +119,7 @@ public Object proceed() throws RuntimeException { cdiContext.setParameters(arguments); } - LOGGER.finest(() -> "Proceeding with CDI interceptors"); + LOGGER.log(System.Logger.Level.TRACE, () -> "Proceeding with CDI interceptors"); return cdiContext.proceed(); } catch (RuntimeException e) { throw e; diff --git a/integrations/micronaut/cdi/src/main/java/module-info.java b/integrations/micronaut/cdi/src/main/java/module-info.java index 29ecde89f11..bc13ef52b71 100644 --- a/integrations/micronaut/cdi/src/main/java/module-info.java +++ b/integrations/micronaut/cdi/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. @@ -22,7 +22,6 @@ requires io.helidon.common; requires jakarta.inject; - requires java.logging; requires microprofile.config.api; requires transitive io.micronaut.aop; diff --git a/integrations/neo4j/neo4j/src/main/java/module-info.java b/integrations/neo4j/neo4j/src/main/java/module-info.java index 74603920ff6..a31d80c979c 100644 --- a/integrations/neo4j/neo4j/src/main/java/module-info.java +++ b/integrations/neo4j/neo4j/src/main/java/module-info.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. @@ -31,7 +31,6 @@ module io.helidon.integrations.neo4j { requires io.helidon.common; - requires java.logging; requires static io.helidon.common.features.api; requires static io.helidon.config.metadata; diff --git a/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java b/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java index 67c1e5db5ab..c088efa59b8 100644 --- a/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java +++ b/integrations/oci/metrics/metrics/src/main/java/io/helidon/integrations/oci/metrics/OciMetricsSupport.java @@ -25,8 +25,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.config.Config; import io.helidon.config.metadata.Configured; @@ -49,7 +47,7 @@ * OCI Metrics Support. */ public class OciMetricsSupport implements HttpService { - private static final Logger LOGGER = Logger.getLogger(OciMetricsSupport.class.getName()); + private static final System.Logger LOGGER = System.getLogger(OciMetricsSupport.class.getName()); private static final UnitConverter STORAGE_UNIT_CONVERTER = UnitConverter.storageUnitConverter(); private static final UnitConverter TIME_UNIT_CONVERTER = UnitConverter.timeUnitConverter(); @@ -183,7 +181,7 @@ private void startExecutor() { private void pushMetrics() { List allMetricDataDetails = ociMetricsData.getMetricDataDetails(); - LOGGER.finest(String.format("Processing %d metrics", allMetricDataDetails.size())); + LOGGER.log(System.Logger.Level.TRACE, String.format("Processing %d metrics", allMetricDataDetails.size())); if (allMetricDataDetails.size() > 0) { while (true) { @@ -214,11 +212,11 @@ private void postBatch(List metricDataDetailsList) { .postMetricDataDetails(postMetricDataDetails) .build(); - LOGGER.finest(String.format("Pushing %d metrics to OCI", metricDataDetailsList.size())); - if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(System.Logger.Level.TRACE, String.format("Pushing %d metrics to OCI", metricDataDetailsList.size())); + if (LOGGER.isLoggable(System.Logger.Level.TRACE)) { metricDataDetailsList .forEach(m -> { - LOGGER.finest(String.format( + LOGGER.log(System.Logger.Level.TRACE, String.format( "Metric details: name=%s, namespace=%s, dimensions=%s, " + "datapoints.timestamp=%s, datapoints.value=%f, metadata=%s", m.getName(), @@ -235,9 +233,10 @@ private void postBatch(List metricDataDetailsList) { this.monitoringClient.setEndpoint( monitoringClient.getEndpoint().replaceFirst("telemetry\\.", "telemetry-ingestion.")); this.monitoringClient.postMetricData(postMetricDataRequest); - LOGGER.finest(String.format("Successfully posted %d metrics to OCI", metricDataDetailsList.size())); + LOGGER.log(System.Logger.Level.TRACE, + String.format("Successfully posted %d metrics to OCI", metricDataDetailsList.size())); } catch (Throwable e) { - LOGGER.warning(String.format("Unable to send metrics to OCI: %s", e.getMessage())); + LOGGER.log(System.Logger.Level.WARNING, String.format("Unable to send metrics to OCI: %s", e.getMessage())); } finally { // restore original endpoint this.monitoringClient.setEndpoint(originalMonitoringEndpoint); @@ -252,16 +251,16 @@ public void routing(HttpRules rules) { @Override public void beforeStart() { if (!enabled) { - LOGGER.info("Metric push to OCI is disabled!"); + LOGGER.log(System.Logger.Level.INFO, "Metric push to OCI is disabled!"); return; } if (scopes.isEmpty()) { - LOGGER.info("No selected metric scopes to push to OCI"); + LOGGER.log(System.Logger.Level.INFO, "No selected metric scopes to push to OCI"); return; } - LOGGER.fine("Starting OCI Metrics agent"); + LOGGER.log(System.Logger.Level.TRACE, "Starting OCI Metrics agent"); ociMetricsData = new OciMetricsData( scopes, nameFormatter, compartmentId, namespace, resourceGroup, descriptionEnabled); @@ -272,7 +271,7 @@ public void beforeStart() { public void afterStop() { // Shutdown executor if created if (scheduledExecutorService != null) { - LOGGER.fine("Shutting down OCI Metrics agent"); + LOGGER.log(System.Logger.Level.TRACE, "Shutting down OCI Metrics agent"); scheduledExecutorService.shutdownNow(); } } 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 81c1a943eaf..63a9f625244 100644 --- a/integrations/oci/metrics/metrics/src/main/java/module-info.java +++ b/integrations/oci/metrics/metrics/src/main/java/module-info.java @@ -21,7 +21,6 @@ module io.helidon.integrations.oci.metrics { requires io.helidon.http; - requires java.logging; requires oci.java.sdk.common; requires static io.helidon.config.metadata; diff --git a/integrations/vault/cdi/src/main/java/module-info.java b/integrations/vault/cdi/src/main/java/module-info.java index 314e636fd2e..670069a078b 100644 --- a/integrations/vault/cdi/src/main/java/module-info.java +++ b/integrations/vault/cdi/src/main/java/module-info.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. @@ -24,7 +24,6 @@ requires io.helidon.microprofile.cdi; requires jakarta.inject; - requires java.logging; requires microprofile.config.api; requires transitive io.helidon.integrations.vault; diff --git a/integrations/vault/secrets/cubbyhole/src/main/java/module-info.java b/integrations/vault/secrets/cubbyhole/src/main/java/module-info.java index 3509e389654..a499c04ed04 100644 --- a/integrations/vault/secrets/cubbyhole/src/main/java/module-info.java +++ b/integrations/vault/secrets/cubbyhole/src/main/java/module-info.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. @@ -28,7 +28,6 @@ module io.helidon.integrations.vault.secrets.cubbyhole { requires io.helidon.integrations.common.rest; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/integrations/vault/secrets/database/src/main/java/module-info.java b/integrations/vault/secrets/database/src/main/java/module-info.java index 2ee65ee7bfa..ecbe8fdfff5 100644 --- a/integrations/vault/secrets/database/src/main/java/module-info.java +++ b/integrations/vault/secrets/database/src/main/java/module-info.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. @@ -27,8 +27,6 @@ ) module io.helidon.integrations.vault.secrets.database { - requires java.logging; - requires static io.helidon.common.features.api; requires transitive io.helidon.integrations.vault; diff --git a/integrations/vault/secrets/kv1/src/main/java/module-info.java b/integrations/vault/secrets/kv1/src/main/java/module-info.java index 60cacf8f706..0673b6700e1 100644 --- a/integrations/vault/secrets/kv1/src/main/java/module-info.java +++ b/integrations/vault/secrets/kv1/src/main/java/module-info.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. @@ -28,7 +28,6 @@ module io.helidon.integrations.vault.secrets.kvone { requires io.helidon.integrations.common.rest; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/integrations/vault/secrets/kv2/src/main/java/module-info.java b/integrations/vault/secrets/kv2/src/main/java/module-info.java index 9c4c27156d7..0189b101435 100644 --- a/integrations/vault/secrets/kv2/src/main/java/module-info.java +++ b/integrations/vault/secrets/kv2/src/main/java/module-info.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. @@ -29,7 +29,6 @@ requires io.helidon.http; requires io.helidon.integrations.common.rest; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/integrations/vault/secrets/pki/src/main/java/module-info.java b/integrations/vault/secrets/pki/src/main/java/module-info.java index 7a4de93f25d..185b9a55d7b 100644 --- a/integrations/vault/secrets/pki/src/main/java/module-info.java +++ b/integrations/vault/secrets/pki/src/main/java/module-info.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. @@ -29,7 +29,6 @@ requires io.helidon.http; requires io.helidon.integrations.common.rest; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/integrations/vault/sys/sys/src/main/java/module-info.java b/integrations/vault/sys/sys/src/main/java/module-info.java index ef1db3e5e3a..2c420a84351 100644 --- a/integrations/vault/sys/sys/src/main/java/module-info.java +++ b/integrations/vault/sys/sys/src/main/java/module-info.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. @@ -31,7 +31,6 @@ requires io.helidon.integrations.common.rest; requires io.helidon.integrations.vault.auths.common; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/jersey/common/src/main/java/module-info.java b/jersey/common/src/main/java/module-info.java index 5ecbd6f4ea5..e6edd3436eb 100644 --- a/jersey/common/src/main/java/module-info.java +++ b/jersey/common/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. @@ -22,7 +22,6 @@ requires io.helidon.common; requires jakarta.annotation; requires jakarta.ws.rs; - requires java.logging; requires jersey.common; requires jersey.server; diff --git a/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java b/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java index ae407b707ff..59c83627782 100644 --- a/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java +++ b/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java @@ -24,7 +24,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.logging.Logger; import io.helidon.common.LazyValue; import io.helidon.common.Version; @@ -62,7 +61,7 @@ import static org.glassfish.jersey.client.ClientProperties.getValue; class HelidonConnector implements Connector { - static final Logger LOGGER = Logger.getLogger(HelidonConnector.class.getName()); + static final System.Logger LOGGER = System.getLogger(HelidonConnector.class.getName()); private static final int DEFAULT_TIMEOUT = 10000; private static final Map EMPTY_MAP_LIST = Map.of("", ""); @@ -308,8 +307,8 @@ static Optional helidonConfig(Configuration configuration) { Object helidonConfig = configuration.getProperty(HelidonProperties.CONFIG); if (helidonConfig != null) { if (!(helidonConfig instanceof Config)) { - LOGGER.warning(String.format("Ignoring Helidon Connector config at '%s'", - HelidonProperties.CONFIG)); + LOGGER.log(System.Logger.Level.WARNING, + String.format("Ignoring Helidon Connector config at '%s'", HelidonProperties.CONFIG)); } else { return Optional.of((Config) helidonConfig); } diff --git a/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.java b/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.java index 88599d35b34..0f6ac3faae4 100644 --- a/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.java +++ b/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.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. @@ -61,7 +61,8 @@ private static Optional createProxy(Object proxyUri, String userName, Str if ("HTTP".equals(u.getScheme().toUpperCase(Locale.ROOT))) { builder.type(Proxy.ProxyType.HTTP); } else { - HelidonConnector.LOGGER.warning(String.format("Proxy schema %s not supported.", u.getScheme())); + HelidonConnector.LOGGER.log(System.Logger.Level.WARNING, + String.format("Proxy schema %s not supported.", u.getScheme())); return Optional.empty(); } } diff --git a/jersey/connector/src/main/java/module-info.java b/jersey/connector/src/main/java/module-info.java index bf224a1f102..6068a39fbef 100644 --- a/jersey/connector/src/main/java/module-info.java +++ b/jersey/connector/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. @@ -26,7 +26,6 @@ requires io.helidon.webclient; requires io.helidon.webclient.http2; requires jakarta.ws.rs; - requires java.logging; requires jersey.common; requires transitive jersey.client; diff --git a/messaging/connectors/aq/src/main/java/module-info.java b/messaging/connectors/aq/src/main/java/module-info.java index 72e12d14b96..3b7feaac8c3 100644 --- a/messaging/connectors/aq/src/main/java/module-info.java +++ b/messaging/connectors/aq/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. @@ -32,7 +32,6 @@ requires aqapi; requires io.helidon.common.configurable; requires io.helidon.messaging.jms.shim; - requires java.logging; requires javax.jms.api; requires jakarta.messaging; diff --git a/messaging/connectors/mock/src/main/java/module-info.java b/messaging/connectors/mock/src/main/java/module-info.java index d1168f62462..2f7181b0a39 100644 --- a/messaging/connectors/mock/src/main/java/module-info.java +++ b/messaging/connectors/mock/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. @@ -21,7 +21,6 @@ module helidon.messaging.mock { requires hamcrest.all; - requires java.logging; requires microprofile.config.api; requires org.reactivestreams; diff --git a/messaging/connectors/wls-jms/src/main/java/module-info.java b/messaging/connectors/wls-jms/src/main/java/module-info.java index 8bf35414d07..aac469befdb 100644 --- a/messaging/connectors/wls-jms/src/main/java/module-info.java +++ b/messaging/connectors/wls-jms/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. @@ -22,7 +22,6 @@ requires io.helidon.messaging.connectors.jms; requires jakarta.messaging; - requires java.logging; requires java.naming; requires static jakarta.cdi; diff --git a/microprofile/cdi/src/main/java/module-info.java b/microprofile/cdi/src/main/java/module-info.java index e996197c76d..bb7dd600330 100644 --- a/microprofile/cdi/src/main/java/module-info.java +++ b/microprofile/cdi/src/main/java/module-info.java @@ -39,7 +39,6 @@ requires io.helidon.config; requires io.helidon.logging.common; requires jakarta.el; // weld requires jakarta.el.ELResolver on module path - requires java.logging; requires java.sql; // weld requires java.sql.Date and we fail if not on classpath requires jdk.unsupported; // needed for Unsafe used from Weld requires microprofile.config.api; diff --git a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/IncomingMethod.java b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/IncomingMethod.java index 6ca7666e20b..4dcc0d2c48a 100644 --- a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/IncomingMethod.java +++ b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/IncomingMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022 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,8 +19,6 @@ import java.lang.reflect.Method; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.common.Errors; import io.helidon.config.Config; @@ -45,7 +43,7 @@ */ class IncomingMethod extends AbstractMessagingMethod implements IncomingMember { - private static final Logger LOGGER = Logger.getLogger(IncomingMethod.class.getName()); + private static final System.Logger LOGGER = System.getLogger(IncomingMethod.class.getName()); private Subscriber subscriber; @@ -102,13 +100,13 @@ public void init(BeanManager beanManager, Config config) { .thenRun(ackCtx::postAck) .exceptionally(t -> { ackCtx.postNack(t); - LOGGER.log(Level.SEVERE, t, - () -> "Error when invoking @Incoming method " + getMethod().getName()); + LOGGER.log(System.Logger.Level.ERROR, + () -> "Error when invoking @Incoming method " + getMethod().getName(), t); return null; }); }) - .onError(t -> LOGGER.log(Level.SEVERE, t, - () -> "Error intercepted on channel " + getIncomingChannelName())) + .onError(t -> LOGGER.log(System.Logger.Level.ERROR, + () -> "Error intercepted on channel " + getIncomingChannelName(), t)) .ignore() .build(); break; @@ -123,21 +121,21 @@ public void init(BeanManager beanManager, Config config) { return ReactiveStreams.fromCompletionStageNullable(result // on error resume .exceptionally(t -> { - LOGGER.log(Level.SEVERE, t, - () -> "Error when invoking @Incoming method " + getMethod().getName()); + LOGGER.log(System.Logger.Level.ERROR, + () -> "Error when invoking @Incoming method " + getMethod().getName(), t); ackCtx.postNack(t); return null; }) .thenRun(ackCtx::postAck)); } catch (Throwable t) { - LOGGER.log(Level.SEVERE, t, - () -> "Error when invoking @Incoming method " + getMethod().getName()); + LOGGER.log(System.Logger.Level.ERROR, + () -> "Error when invoking @Incoming method " + getMethod().getName(), t); ackCtx.postNack(t); return ReactiveStreams.empty(); } }) - .onError(t -> LOGGER.log(Level.SEVERE, t, - () -> "Error intercepted in channel " + getIncomingChannelName())) + .onError(t -> LOGGER.log(System.Logger.Level.ERROR, + () -> "Error intercepted in channel " + getIncomingChannelName(), t)) .ignore() .build(); break; @@ -157,14 +155,14 @@ public void init(BeanManager beanManager, Config config) { }) .thenRun(ackCtx::postAck)); } catch (Throwable t) { - LOGGER.log(Level.SEVERE, t, - () -> "Error when invoking @Incoming method " + getMethod().getName()); + LOGGER.log(System.Logger.Level.ERROR, + () -> "Error when invoking @Incoming method " + getMethod().getName(), t); ackCtx.postNack(t); return ReactiveStreams.empty(); } }) - .onError(t -> LOGGER.log(Level.SEVERE, t, - () -> "Error intercepted in channel " + getIncomingChannelName())) + .onError(t -> LOGGER.log(System.Logger.Level.ERROR, + () -> "Error intercepted in channel " + getIncomingChannelName(), t)) .ignore() .build(); break; diff --git a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/LatestEmitter.java b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/LatestEmitter.java index ce37bea46e8..c78984a22fb 100644 --- a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/LatestEmitter.java +++ b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/LatestEmitter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 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. @@ -18,8 +18,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.common.reactive.BufferedEmittingPublisher; @@ -33,7 +31,7 @@ */ class LatestEmitter extends OutgoingEmitter { - private static final Logger LOGGER = Logger.getLogger(LatestEmitter.class.getName()); + private static final System.Logger LOGGER = System.getLogger(LatestEmitter.class.getName()); private final ConcurrentLinkedDeque buffer = new ConcurrentLinkedDeque<>(); private final BufferedEmittingPublisher bep = BufferedEmittingPublisher.builder() @@ -121,8 +119,8 @@ public void validate(Object payload) { long bufferLimit = getBufferLimit(); if (bufferLimit > 0 && bufferSize >= bufferLimit) { Object dropped = dropFirst(); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "Dropped first item: " + MessageUtils.unwrap(dropped)); + if (LOGGER.isLoggable(System.Logger.Level.TRACE)) { + LOGGER.log(System.Logger.Level.TRACE, "Dropped first item: " + MessageUtils.unwrap(dropped)); } } } diff --git a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProcessorMethod.java b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProcessorMethod.java index 2303fdc955b..a43e96e663a 100644 --- a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProcessorMethod.java +++ b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProcessorMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022 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,8 +20,6 @@ import java.lang.reflect.Method; import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.common.Errors; import io.helidon.common.LazyValue; @@ -42,7 +40,7 @@ class ProcessorMethod extends AbstractMessagingMethod implements OutgoingMember, IncomingMember { - private static final Logger LOGGER = Logger.getLogger(ProcessorMethod.class.getName()); + private static final System.Logger LOGGER = System.getLogger(ProcessorMethod.class.getName()); private final LazyValue compatibilityMode = LazyValue.create(() -> ConfigProvider.getConfig().getOptionalValue("mp.messaging.helidon.propagate-errors", Boolean.class) @@ -137,7 +135,7 @@ private PublisherBuilder resumeOnError(Throwable t) { if (compatibilityMode.get()) { return ReactiveStreams.failed(t); } - LOGGER.log(Level.SEVERE, "Error intercepted in processor method " + LOGGER.log(System.Logger.Level.ERROR, "Error intercepted in processor method " + this.getMethod().getDeclaringClass().getSimpleName() + "#" + this.getMethod().getName() + " incoming channel: " + this.getIncomingChannelName() + " outgoing channel: " + this.getOutgoingChannelName(), t); diff --git a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProxySubscriber.java b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProxySubscriber.java index 7e07ced0f67..7547a494fcd 100644 --- a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProxySubscriber.java +++ b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/ProxySubscriber.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,9 +16,6 @@ package io.helidon.microprofile.messaging; -import java.util.logging.Level; -import java.util.logging.Logger; - import org.eclipse.microprofile.reactive.messaging.Message; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -30,7 +27,7 @@ */ class ProxySubscriber implements Subscriber { - private static final Logger LOGGER = Logger.getLogger(ProxySubscriber.class.getName()); + private static final System.Logger LOGGER = System.getLogger(ProxySubscriber.class.getName()); private final IncomingMethod method; private final Subscriber originalSubscriber; @@ -69,7 +66,7 @@ public void onNext(Object item) { ackCtx.postAck(); } catch (Throwable t) { ackCtx.postNack(t); - LOGGER.log(Level.SEVERE, "Error thrown by subscriber on channel " + method.getIncomingChannelName(), t); + LOGGER.log(System.Logger.Level.ERROR, "Error thrown by subscriber on channel " + method.getIncomingChannelName(), t); } } diff --git a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/UniversalChannel.java b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/UniversalChannel.java index e34020966c0..0ae2a5ff823 100644 --- a/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/UniversalChannel.java +++ b/microprofile/messaging/core/src/main/java/io/helidon/microprofile/messaging/UniversalChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022 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. @@ -17,7 +17,6 @@ package io.helidon.microprofile.messaging; import java.util.Optional; -import java.util.logging.Logger; import io.helidon.config.Config; import io.helidon.config.ConfigValue; @@ -29,7 +28,7 @@ class UniversalChannel { - private static final Logger LOGGER = Logger.getLogger(UniversalChannel.class.getName()); + private static final System.Logger LOGGER = System.getLogger(UniversalChannel.class.getName()); private String name; private IncomingMember incomingMember; @@ -124,7 +123,7 @@ void connect() { .append(name).append(" with outgoing method "); if (outgoingMember == null) { - LOGGER.severe(connectMessage.append("and no outgoing method found!").toString()); + LOGGER.log(System.Logger.Level.ERROR, connectMessage.append("and no outgoing method found!").toString()); throw ExceptionUtils.createNoOutgoingMethodForChannel(name); } @@ -140,7 +139,7 @@ void connect() { var optUpstreamChannel = Optional.ofNullable(this.upstreamChannel); if (incomingMember == null) { - LOGGER.severe(connectMessage.append("and no incoming method found!").toString()); + LOGGER.log(System.Logger.Level.ERROR, connectMessage.append("and no incoming method found!").toString()); throw ExceptionUtils.createNoIncomingMethodForChannel(name); } diff --git a/microprofile/messaging/core/src/main/java/module-info.java b/microprofile/messaging/core/src/main/java/module-info.java index 3b25d7a904e..e0874ed2cad 100644 --- a/microprofile/messaging/core/src/main/java/module-info.java +++ b/microprofile/messaging/core/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -37,7 +37,6 @@ requires io.helidon.microprofile.reactive; requires io.helidon.microprofile.server; requires jakarta.inject; - requires java.logging; requires microprofile.reactive.messaging.api; requires microprofile.reactive.streams.operators.api; diff --git a/microprofile/messaging/health/src/main/java/module-info.java b/microprofile/messaging/health/src/main/java/module-info.java index 080d3370d11..9de8d8e4a7e 100644 --- a/microprofile/messaging/health/src/main/java/module-info.java +++ b/microprofile/messaging/health/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. @@ -21,7 +21,6 @@ module io.helidon.microprofile.messaging.health { requires jakarta.inject; - requires java.logging; requires microprofile.health.api; requires transitive io.helidon.microprofile.health; diff --git a/microprofile/messaging/metrics/src/main/java/module-info.java b/microprofile/messaging/metrics/src/main/java/module-info.java index c61b58ae1de..d15ab403a5b 100644 --- a/microprofile/messaging/metrics/src/main/java/module-info.java +++ b/microprofile/messaging/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. @@ -22,7 +22,6 @@ requires jakarta.cdi; requires jakarta.inject; - requires java.logging; requires transitive io.helidon.microprofile.messaging; requires transitive io.helidon.microprofile.metrics; diff --git a/microprofile/oidc/src/main/java/module-info.java b/microprofile/oidc/src/main/java/module-info.java index 966d1e4c99e..c784e801d7d 100644 --- a/microprofile/oidc/src/main/java/module-info.java +++ b/microprofile/oidc/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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,7 +22,6 @@ requires io.helidon.microprofile.security; requires io.helidon.microprofile.server; requires io.helidon.security.providers.oidc; - requires java.logging; requires transitive jakarta.cdi; diff --git a/microprofile/reactive-streams/src/main/java/module-info.java b/microprofile/reactive-streams/src/main/java/module-info.java index 2fc734cb3ba..18ea7c2d33f 100644 --- a/microprofile/reactive-streams/src/main/java/module-info.java +++ b/microprofile/reactive-streams/src/main/java/module-info.java @@ -31,7 +31,6 @@ module io.helidon.microprofile.reactive { requires io.helidon.common.reactive; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/microprofile/telemetry/src/main/java/module-info.java b/microprofile/telemetry/src/main/java/module-info.java index cb1ddfe82b9..766b3b7d6d5 100644 --- a/microprofile/telemetry/src/main/java/module-info.java +++ b/microprofile/telemetry/src/main/java/module-info.java @@ -46,7 +46,6 @@ requires io.opentelemetry.sdk; requires jakarta.annotation; requires jakarta.inject; - requires java.logging; requires microprofile.config.api; requires opentelemetry.instrumentation.annotations; diff --git a/security/integration/common/src/main/java/module-info.java b/security/integration/common/src/main/java/module-info.java index be04bdfe1e3..1b0e6fc595e 100644 --- a/security/integration/common/src/main/java/module-info.java +++ b/security/integration/common/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. @@ -20,7 +20,6 @@ module io.helidon.security.integration.common { requires io.helidon.common.context; - requires java.logging; requires transitive io.helidon.security; requires transitive io.helidon.tracing.config; diff --git a/security/security/src/main/java/io/helidon/security/DefaultAuditProvider.java b/security/security/src/main/java/io/helidon/security/DefaultAuditProvider.java index d77176fc776..7bb19dc4f7a 100644 --- a/security/security/src/main/java/io/helidon/security/DefaultAuditProvider.java +++ b/security/security/src/main/java/io/helidon/security/DefaultAuditProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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,8 +19,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.common.config.Config; import io.helidon.security.spi.AuditProvider; @@ -29,32 +27,32 @@ * Default implementation of audit provider. */ final class DefaultAuditProvider implements AuditProvider { - private final Logger auditLogger; - private final Level failureLevel; - private final Level successLevel; - private final Level infoLevel; - private final Level warnLevel; - private final Level errorLevel; - private final Level auditFailureLevel; + private final System.Logger auditLogger; + private final System.Logger.Level failureLevel; + private final System.Logger.Level successLevel; + private final System.Logger.Level infoLevel; + private final System.Logger.Level warnLevel; + private final System.Logger.Level errorLevel; + private final System.Logger.Level auditFailureLevel; private DefaultAuditProvider(Config config) { // config node is already located on the security node - this.auditLogger = Logger.getLogger(config.get("audit.defaultProvider.logger") + this.auditLogger = System.getLogger(config.get("audit.defaultProvider.logger") .asString() .orElse("AUDIT")); - this.failureLevel = level(config, "failure", Level.FINEST); - this.successLevel = level(config, "success", Level.FINEST); - this.infoLevel = level(config, "info", Level.FINEST); - this.warnLevel = level(config, "warn", Level.WARNING); - this.errorLevel = level(config, "error", Level.SEVERE); - this.auditFailureLevel = level(config, "audit-failure", Level.SEVERE); + this.failureLevel = level(config, "failure", System.Logger.Level.TRACE); + this.successLevel = level(config, "success", System.Logger.Level.TRACE); + this.infoLevel = level(config, "info", System.Logger.Level.TRACE); + this.warnLevel = level(config, "warn", System.Logger.Level.WARNING); + this.errorLevel = level(config, "error", System.Logger.Level.ERROR); + this.auditFailureLevel = level(config, "audit-failure", System.Logger.Level.ERROR); } - private Level level(Config config, String auditSeverity, Level defaultLevel) { + private System.Logger.Level level(Config config, String auditSeverity, System.Logger.Level defaultLevel) { return config.get("audit.defaultProvider.level." + auditSeverity) .asString() - .map(Level::parse) + .map(s -> System.Logger.Level.valueOf(s)) .orElse(defaultLevel); } @@ -68,7 +66,7 @@ public Consumer auditConsumer() { } private void audit(TracedAuditEvent event) { - Level level; + System.Logger.Level level; switch (event.severity()) { case FAILURE: @@ -96,7 +94,7 @@ private void audit(TracedAuditEvent event) { logEvent(event, level); } - private void logEvent(TracedAuditEvent event, Level level) { + private void logEvent(TracedAuditEvent event, System.Logger.Level level) { if (!auditLogger.isLoggable(level)) { // no need to create the message when the message would not be logged return; diff --git a/security/security/src/main/java/io/helidon/security/ProviderRequest.java b/security/security/src/main/java/io/helidon/security/ProviderRequest.java index 7884e41fade..38be514b85d 100644 --- a/security/security/src/main/java/io/helidon/security/ProviderRequest.java +++ b/security/security/src/main/java/io/helidon/security/ProviderRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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.Map; import java.util.Optional; import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import io.helidon.security.util.AbacSupport; @@ -44,7 +42,7 @@ * */ public class ProviderRequest implements AbacSupport { - private static final Logger LOGGER = Logger.getLogger(ProviderRequest.class.getName()); + private static final System.Logger LOGGER = System.getLogger(ProviderRequest.class.getName()); private final Map contextRoot = new HashMap<>(); private final Optional subject; @@ -109,10 +107,12 @@ public static Optional getValue(Object object, String key) { } } catch (NoSuchFieldException e) { // ignore, field is not present, we try accessor methods - LOGGER.log(Level.FINEST, e, () -> "Field \"" + key + "\" + is not present in class: " + aClass.getName()); + LOGGER.log(System.Logger.Level.TRACE, () -> + "Field \"" + key + "\" + is not present in class: " + aClass.getName(), e); } catch (IllegalAccessException e) { // ignore, we check access first - LOGGER.log(Level.FINEST, e, () -> "Failed to access field: \"" + key + "\" in class: " + aClass.getName()); + LOGGER.log(System.Logger.Level.TRACE, () -> + "Failed to access field: \"" + key + "\" in class: " + aClass.getName(), e); } //now check accessor methods @@ -150,7 +150,8 @@ static Optional getMethod(Class aClass, String methodName) { return Optional.empty(); } catch (NoSuchMethodException e) { // method is not present - LOGGER.log(Level.FINEST, e, () -> "Method: \"" + methodName + "\" is not in class: " + aClass.getName()); + LOGGER.log(System.Logger.Level.TRACE, + () -> "Method: \"" + methodName + "\" is not in class: " + aClass.getName(), e); return Optional.empty(); } } diff --git a/security/security/src/main/java/io/helidon/security/SecurityImpl.java b/security/security/src/main/java/io/helidon/security/SecurityImpl.java index dfd67c79761..11aa89e4ad2 100644 --- a/security/security/src/main/java/io/helidon/security/SecurityImpl.java +++ b/security/security/src/main/java/io/helidon/security/SecurityImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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,7 +26,6 @@ import java.util.UUID; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.logging.Logger; import io.helidon.common.LazyValue; import io.helidon.common.config.Config; @@ -59,7 +58,7 @@ final class SecurityImpl implements Security { "environment" ); - private static final Logger LOGGER = Logger.getLogger(SecurityImpl.class.getName()); + private static final System.Logger LOGGER = System.getLogger(SecurityImpl.class.getName()); private final Collection> annotations = new LinkedList<>(); private final List> auditors = new LinkedList<>(); @@ -265,7 +264,8 @@ public Optional secret(String configurationName) { public String secret(String configurationName, String defaultValue) { Supplier> supplier = secrets.get(configurationName); if (supplier == null) { - LOGGER.finest(() -> "There is no configured secret named " + configurationName + ", using default value"); + LOGGER.log(System.Logger.Level.TRACE, + () -> "There is no configured secret named " + configurationName + ", using default value"); return defaultValue; } diff --git a/security/security/src/main/java/io/helidon/security/SecurityUtil.java b/security/security/src/main/java/io/helidon/security/SecurityUtil.java index 17aed56c583..5e10d52b1f6 100644 --- a/security/security/src/main/java/io/helidon/security/SecurityUtil.java +++ b/security/security/src/main/java/io/helidon/security/SecurityUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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.Map; import java.util.Optional; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import io.helidon.common.config.Config; @@ -37,7 +35,7 @@ * Utility class for internal needs. */ final class SecurityUtil { - private static final Logger LOGGER = Logger.getLogger(SecurityUtil.class.getName()); + private static final System.Logger LOGGER = System.getLogger(SecurityUtil.class.getName()); private SecurityUtil() { } @@ -127,10 +125,9 @@ static T instantiate(String className, Class type, Config confi } catch (ClassCastException e) { throw new SecurityException("Class " + className + " is not instance of expected type: " + type.getName()); } catch (ConfigException e) { - LOGGER.log(Level.FINEST, - e, + LOGGER.log(System.Logger.Level.TRACE, () -> "Class " + className + " failed to get mapped by config. Will attempt public default " - + "constructor"); + + "constructor", e); configException = e; } } @@ -139,8 +136,8 @@ static T instantiate(String className, Class type, Config confi try { return type.cast(clazz.getConstructor().newInstance()); } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Could not instantiate: " + className + ". Class must either have a default public" - + " constructor or be mappable by Config"); + LOGGER.log(System.Logger.Level.TRACE, "Could not instantiate: " + className + + ". Class must either have a default public constructor or be mappable by Config"); configException = ((null == configException) ? e : configException); throw new SecurityException("Failed to load " + type diff --git a/security/security/src/main/java/io/helidon/security/spi/AuditProvider.java b/security/security/src/main/java/io/helidon/security/spi/AuditProvider.java index cbd6ab30fda..67fce0eaf4c 100644 --- a/security/security/src/main/java/io/helidon/security/spi/AuditProvider.java +++ b/security/security/src/main/java/io/helidon/security/spi/AuditProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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,11 @@ * If no custom audit provider is defined (using * {@link io.helidon.security.Security.Builder#addAuditProvider(AuditProvider)}) a default provider will be used. *

    - * Default audit provider logs most events in {@link java.util.logging.Level#FINEST}. + * Default audit provider logs most events in {@link System.Logger.Level#TRACE}. * {@link AuditEvent.AuditSeverity#AUDIT_FAILURE} * and {@link AuditEvent.AuditSeverity#ERROR} - * are logged in {@link java.util.logging.Level#SEVERE} and {@link AuditEvent.AuditSeverity#WARN} is logged in {@link - * java.util.logging.Level#WARNING} level. + * are logged in {@link System.Logger.Level#ERROR} and {@link AuditEvent.AuditSeverity#WARN} is logged in {@link + * System.Logger.Level#WARNING} level. * *

    * Format of default audit provider log record (all end of lines are removed from message, not from stack trace): diff --git a/security/security/src/main/java/module-info.java b/security/security/src/main/java/module-info.java index 4eb5370e385..0edb412368c 100644 --- a/security/security/src/main/java/module-info.java +++ b/security/security/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. @@ -31,7 +31,6 @@ requires io.helidon.common.context; requires io.helidon.common.uri; - requires java.logging; requires static io.helidon.common.features.api; requires static io.helidon.config.metadata; diff --git a/tests/apps/bookstore/bookstore-mp/src/main/java/module-info.java b/tests/apps/bookstore/bookstore-mp/src/main/java/module-info.java index 4117a3a8ba1..1384d661f59 100644 --- a/tests/apps/bookstore/bookstore-mp/src/main/java/module-info.java +++ b/tests/apps/bookstore/bookstore-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. @@ -27,7 +27,6 @@ requires io.helidon.tests.apps.bookstore.common; requires jakarta.json.bind; requires jakarta.json; - requires java.logging; opens io.helidon.tests.apps.bookstore.mp to io.helidon.microprofile.cdi,weld.core.impl; diff --git a/tests/apps/bookstore/bookstore-se/src/main/java/module-info.java b/tests/apps/bookstore/bookstore-se/src/main/java/module-info.java index adeefd59b1d..b5055cf9250 100644 --- a/tests/apps/bookstore/bookstore-se/src/main/java/module-info.java +++ b/tests/apps/bookstore/bookstore-se/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. @@ -34,7 +34,6 @@ requires io.helidon.webserver.observe.metrics; requires io.helidon.webserver; requires jakarta.json; - requires java.logging; exports io.helidon.tests.apps.bookstore.se; diff --git a/tests/integration/native-image/mp-3/src/main/java/module-info.java b/tests/integration/native-image/mp-3/src/main/java/module-info.java index 5e01f0521b9..4093f443cdb 100644 --- a/tests/integration/native-image/mp-3/src/main/java/module-info.java +++ b/tests/integration/native-image/mp-3/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. @@ -18,8 +18,7 @@ * Quickstart MicroProfile example. */ module helidon.tests.nimage.quickstartmp { - - requires java.logging; + requires io.helidon.microprofile.bundle; exports io.helidon.tests.integration.nativeimage.mp3; diff --git a/tracing/jersey-client/src/main/java/module-info.java b/tracing/jersey-client/src/main/java/module-info.java index 24742455ad4..4047ca3a553 100644 --- a/tracing/jersey-client/src/main/java/module-info.java +++ b/tracing/jersey-client/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. @@ -33,7 +33,6 @@ requires io.helidon.tracing.config; requires io.helidon.tracing; requires jakarta.annotation; - requires java.logging; requires jersey.common; requires static io.helidon.common.features.api; diff --git a/tracing/providers/zipkin/src/main/java/module-info.java b/tracing/providers/zipkin/src/main/java/module-info.java index dae560d611c..afc01904698 100644 --- a/tracing/providers/zipkin/src/main/java/module-info.java +++ b/tracing/providers/zipkin/src/main/java/module-info.java @@ -33,7 +33,6 @@ requires io.helidon.tracing.providers.opentracing; requires io.opentracing.noop; requires io.opentracing.util; - requires java.logging; requires static io.helidon.common.features.api; requires static io.helidon.config.metadata; diff --git a/webserver/graphql/src/main/java/module-info.java b/webserver/graphql/src/main/java/module-info.java index 320d1bdff2a..b59202c21b6 100644 --- a/webserver/graphql/src/main/java/module-info.java +++ b/webserver/graphql/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. @@ -38,7 +38,6 @@ requires io.helidon.graphql.server; requires io.helidon.webserver.cors; requires jakarta.json.bind; - requires java.logging; requires org.eclipse.yasson; requires static io.helidon.common.features.api; diff --git a/webserver/grpc/src/main/java/module-info.java b/webserver/grpc/src/main/java/module-info.java index 152d3b7d38f..db94d3070ee 100644 --- a/webserver/grpc/src/main/java/module-info.java +++ b/webserver/grpc/src/main/java/module-info.java @@ -34,7 +34,6 @@ requires io.grpc.protobuf.lite; requires io.helidon.builder.api; requires io.helidon.webserver.http2; - requires java.logging; requires static io.helidon.common.features.api; diff --git a/webserver/observe/log/src/main/java/module-info.java b/webserver/observe/log/src/main/java/module-info.java index a9b48c16197..9412ac694fa 100644 --- a/webserver/observe/log/src/main/java/module-info.java +++ b/webserver/observe/log/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. @@ -41,5 +41,4 @@ provides io.helidon.webserver.observe.spi.ObserveProvider with io.helidon.webserver.observe.log.LogObserveProvider; - } \ No newline at end of file diff --git a/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHandler.java b/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHandler.java index d5a1057bc8d..7debb2d2800 100644 --- a/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHandler.java +++ b/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; import io.helidon.builder.api.RuntimeType; import io.helidon.common.config.Config; @@ -74,7 +72,7 @@ public final class SecurityHandler implements Handler, RuntimeType.Api { static final String DEFAULT_AUDIT_EVENT_TYPE = "request"; static final String DEFAULT_AUDIT_MESSAGE_FORMAT = "%3$s %1$s \"%2$s\" %5$s %6$s requested by %4$s"; - private static final Logger LOGGER = Logger.getLogger(SecurityHandler.class.getName()); + private static final System.Logger LOGGER = System.getLogger(SecurityHandler.class.getName()); private static final SecurityHandler DEFAULT_INSTANCE = builder().build(); private final SecurityHandlerConfig config; @@ -499,7 +497,7 @@ private void processSecurity(SecurityContext securityContext, ServerRequest req, } } catch (Exception e) { tracing.error(e); - LOGGER.log(Level.SEVERE, "Unexpected exception during security processing", e); + LOGGER.log(System.Logger.Level.ERROR, "Unexpected exception during security processing", e); abortRequest(res, null, Status.INTERNAL_SERVER_ERROR_500.code(), Map.of()); } @@ -620,7 +618,7 @@ private void atnSpanFinish(AtnTracing atnTracing, AuthenticationResponse respons private boolean atnAbstainFailure(ServerResponse res, AuthenticationResponse response) { if (authenticationOptional.orElse(false)) { - LOGGER.finest("Authentication failed, but was optional, so assuming anonymous"); + LOGGER.log(System.Logger.Level.TRACE, "Authentication failed, but was optional, so assuming anonymous"); return false; } @@ -634,7 +632,7 @@ private boolean atnAbstainFailure(ServerResponse res, AuthenticationResponse res private boolean atnFinishFailure(ServerResponse res, AuthenticationResponse response) { if (authenticationOptional.orElse(false)) { - LOGGER.finest("Authentication failed, but was optional, so assuming anonymous"); + LOGGER.log(System.Logger.Level.TRACE, "Authentication failed, but was optional, so assuming anonymous"); return false; } else { int defaultStatusCode = Status.UNAUTHORIZED_401.code(); diff --git a/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHttpFeature.java b/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHttpFeature.java index 054702197b4..1b08bd0aaea 100644 --- a/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHttpFeature.java +++ b/webserver/security/src/main/java/io/helidon/webserver/security/SecurityHttpFeature.java @@ -19,7 +19,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.logging.Logger; import io.helidon.common.Weighted; import io.helidon.common.config.Config; @@ -99,7 +98,7 @@ public final class SecurityHttpFeature implements HttpSecurity, HttpFeature, Wei */ public static final String CONTEXT_RESPONSE_HEADERS = "security.responseHeaders"; - private static final Logger LOGGER = Logger.getLogger(SecurityHttpFeature.class.getName()); + private static final System.Logger LOGGER = System.getLogger(SecurityHttpFeature.class.getName()); private final Security security; private final SecurityHandler defaultHandler; @@ -174,7 +173,7 @@ public SecurityHttpFeature securityDefaults(SecurityHandler defaultHandler) { @Override public void setup(HttpRouting.Builder rules) { if (!security.enabled()) { - LOGGER.info("Security is disabled. Not registering any security handlers"); + LOGGER.log(System.Logger.Level.INFO, "Security is disabled. Not registering any security handlers"); return; } rules.security(this); diff --git a/webserver/security/src/main/java/module-info.java b/webserver/security/src/main/java/module-info.java index b04ade996b6..ca67186a9bb 100644 --- a/webserver/security/src/main/java/module-info.java +++ b/webserver/security/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 @@ requires io.helidon.common.context; requires io.helidon.security.integration.common; requires io.helidon.webserver; - requires java.logging; requires transitive io.helidon.builder.api; requires transitive io.helidon.common; From d0d7c64656bfdfc297ae087f8dde6cbe34106510 Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Wed, 29 May 2024 09:00:24 -0700 Subject: [PATCH 030/245] 4.x: Upgrade to MP Config 3.1 and fix an issue with profile specific properties (#8757) * Correctly handle profile-specific properties * Add test for profile specific properties * Upgrade MP Config to 3.1 * Return a ConfigValue even when expression on rhs does not resolve * Update docs for microprofile config 3.1 * Add ConfigValue test for missing expression --- .../io/helidon/config/mp/MpConfigImpl.java | 33 +++++++------ .../config/mp/MpConfigReferenceTest.java | 14 +++++- .../config/mp/MpConfigSourcesTest.java | 49 +++++++++++++++++++ dependencies/pom.xml | 3 +- .../main/asciidoc/includes/attributes.adoc | 2 +- .../main/asciidoc/mp/config/introduction.adoc | 2 +- 6 files changed, 82 insertions(+), 21 deletions(-) diff --git a/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigImpl.java b/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigImpl.java index 44d8854af48..8299a1c1cc5 100644 --- a/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigImpl.java +++ b/config/config-mp/src/main/java/io/helidon/config/mp/MpConfigImpl.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. @@ -107,13 +107,18 @@ class MpConfigImpl implements Config { @Override public ConfigValue getConfigValue(String key) { + + ConfigValue value = findConfigValue(key) + .orElse(new ConfigValueImpl(key, null, null, null, 0)); + if (configProfile == null) { - return findConfigValue(key) - .orElseGet(() -> new ConfigValueImpl(key, null, null, null, 0)); + return value; } - return findConfigValue("%" + configProfile + "." + key) - .or(() -> findConfigValue(key)) - .orElseGet(() -> new ConfigValueImpl(key, null, null, null, 0)); + + ConfigValue profileValue = findConfigValue("%" + configProfile + "." + key) + .orElse(value); + + return value.getSourceOrdinal() > profileValue.getSourceOrdinal() ? value : profileValue; } @Override @@ -126,12 +131,7 @@ public T getValue(String propertyName, Class propertyType) { @SuppressWarnings("unchecked") @Override public Optional getOptionalValue(String propertyName, Class propertyType) { - if (configProfile == null) { - return optionalValue(propertyName, propertyType); - } - - return optionalValue("%" + configProfile + "." + propertyName, propertyType) - .or(() -> optionalValue(propertyName, propertyType)); + return optionalValue(propertyName, propertyType); } @SuppressWarnings("unchecked") @@ -187,9 +187,9 @@ private Optional optionalValue(String propertyName, Class propertyType return Optional.empty(); } } else { - return findConfigValue(propertyName) - .map(ConfigValue::getValue) - .map(it -> convert(propertyName, propertyType, it)); + Optional value = Optional.of(getConfigValue(propertyName)); + return value.map(ConfigValue::getValue) + .map(it -> convert(propertyName, propertyType, it)); } } @@ -314,6 +314,7 @@ private T convert(String propertyName, Class type, String value) { } private Optional findConfigValue(String propertyName) { + for (ConfigSource source : sources) { String value = source.getValue(propertyName); @@ -341,7 +342,7 @@ private Optional findConfigValue(String propertyName) { .map(it -> new ConfigValueImpl(propertyName, it, rawValue, source.getName(), source.getOrdinal())); } catch (NoSuchElementException e) { // Property expression does not resolve - return Optional.empty(); + return Optional.of(new ConfigValueImpl(propertyName, null, rawValue, source.getName(), source.getOrdinal())); } } diff --git a/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigReferenceTest.java b/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigReferenceTest.java index 6d7cb096542..184fd701b29 100644 --- a/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigReferenceTest.java +++ b/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigReferenceTest.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,13 +20,16 @@ import java.util.Optional; import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigValue; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertThrows; public class MpConfigReferenceTest { @@ -65,6 +68,15 @@ void testMissingRefs() { // since Config 2.0, missing references must throw an exception assertThrows(NoSuchElementException.class, () -> config.getValue("referencing4-1", String.class)); assertThrows(NoSuchElementException.class, () -> config.getValue( "referencing4-2", String.class)); + + // MP Config 3.1 TCK requires well-formed ConfigValue when missing reference + ConfigValue configValue = config.getConfigValue("referencing4-1"); + assertThat(configValue, notNullValue()); + assertThat(configValue.getName(), is("referencing4-1")); + assertThat(configValue.getValue(), nullValue()); + assertThat(configValue.getRawValue(), is("${missing}")); + assertThat(configValue.getSourceName(), endsWith("microprofile-config.properties")); + assertThat(configValue.getSourceOrdinal(), is(100)); } @Test diff --git a/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java b/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java index ac43156af3d..b9823e8671d 100644 --- a/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java +++ b/config/config-mp/src/test/java/io/helidon/config/mp/MpConfigSourcesTest.java @@ -19,6 +19,9 @@ import java.io.ByteArrayInputStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -60,6 +63,52 @@ void testHelidonMap() { assertThat(mpSource.getValue("key.third"), is("<>{}().,:;/|\\~`?!@#$%^&*-=+*")); } + @Test + void testProfileSpecificProperty() { + Map values = Map.of( + "%dev.vehicle.name", "car", + "vehicle.name", "bike", + "%dev.vehicle.color", "blue", + "vehicle.color", "red", + "%dev.vehicle.size", "large" + ); + org.eclipse.microprofile.config.spi.ConfigSource mapSource = MpConfigSources.create(ConfigSources.create(values).build()); + assertThat(mapSource.getOrdinal(), is(100)); + assertThat(mapSource.getValue("vehicle.name"), is("bike")); + + // One data source. The profile specific property should take precedence + MpConfigImpl config = new MpConfigImpl(List.of(mapSource), new HashMap<>(), Collections.emptyList(), "dev"); + assertThat(config.getConfigValue("vehicle.name").getValue(), is("car")); + assertThat(config.getOptionalValue("vehicle.name", String.class).orElse("error"), is("car")); + + System.setProperty("vehicle.name", "jet"); + System.setProperty("%dev.vehicle.make", "tucker"); + org.eclipse.microprofile.config.spi.ConfigSource propertySource = MpConfigSources.systemProperties(); + assertThat(propertySource.getOrdinal(), is(400)); + assertThat(propertySource.getValue("vehicle.name"), is("jet")); + + // Create Config from both data sources with the "dev" profile + config = new MpConfigImpl(List.of(propertySource, mapSource), new HashMap<>(), Collections.emptyList(), "dev"); + + // The vanilla property in the higher ordinal data source should trump the profile specific property in the + // lower ordinal data source + assertThat(config.getConfigValue("vehicle.name").getValue(), is("jet")); + assertThat(config.getOptionalValue("vehicle.name", String.class).orElse("error"), is("jet")); + + // Within one DataSource the profile specific property takes precedence + assertThat(config.getConfigValue("vehicle.color").getValue(), is("blue")); + assertThat(config.getOptionalValue("vehicle.color", String.class).orElse("error"), is("blue")); + + // Make sure missing vanilla values do not mess things up + assertThat(config.getConfigValue("vehicle.size").getValue(), is("large")); + assertThat(config.getOptionalValue("vehicle.size", String.class).orElse("error"), is("large")); + assertThat(config.getConfigValue("vehicle.make").getValue(), is("tucker")); + assertThat(config.getOptionalValue("vehicle.make", String.class).orElse("error"), is("tucker")); + + System.clearProperty("vehicle.name"); + System.clearProperty("%dev.vehicle.name"); + } + @Test void testHelidonParsable() { ParsableImpl helidonSource = new ParsableImpl(); diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 707bea33221..546a528f132 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -104,8 +104,7 @@ 3.8.7 3.4.3 4.8.0 - - 3.0.3 + 3.1 4.0.2 2.0 diff --git a/docs/src/main/asciidoc/includes/attributes.adoc b/docs/src/main/asciidoc/includes/attributes.adoc index 9043e83f097..3a93fa520ec 100644 --- a/docs/src/main/asciidoc/includes/attributes.adoc +++ b/docs/src/main/asciidoc/includes/attributes.adoc @@ -43,7 +43,7 @@ endif::[] // microprofile specifications :version-lib-microprofile-lra-api: 2.0 -:version-lib-microprofile-config: 3.0.3 +:version-lib-microprofile-config: 3.1 :version-lib-microprofile-fault-tolerance-api: 4.0.2 :version-lib-microprofile-graphql: 2.0 :version-lib-microprofile-health: 4.0 diff --git a/docs/src/main/asciidoc/mp/config/introduction.adoc b/docs/src/main/asciidoc/mp/config/introduction.adoc index 3141573b223..d839fe7d976 100644 --- a/docs/src/main/asciidoc/mp/config/introduction.adoc +++ b/docs/src/main/asciidoc/mp/config/introduction.adoc @@ -243,4 +243,4 @@ Step-by-step guide about using {spec-name} in your Helidon MP application. == Reference * link:{microprofile-config-spec-url}[{spec-name} Specifications] -* link:{microprofile-fault-tolerance-javadoc-url}[{spec-name} Javadocs] +* link:{microprofile-config-javadoc-url}[{spec-name} Javadocs] From 62adcbdc0c46ffcafff0d703bcfa4b3895e11679 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Thu, 30 May 2024 09:40:20 -0400 Subject: [PATCH 031/245] gRPC Client Implementation (#8423) * Initial commit based on in-progress work from Tomas. Signed-off-by: Santiago Pericasgeertsen * Additional work arounds tests and descriptors. Signed-off-by: Santiago Pericasgeertsen * Simple unary method call without using stubs. Signed-off-by: Santiago Pericasgeertsen * Basic support for unary, serverStream, streamClient and bidi invocations. Signed-off-by: Santiago Pericasgeertsen * Support for sync and async gRPC calls. Signed-off-by: Santiago Pericasgeertsen * Fixed problem with EOS and improved logging. All basic tests are passing. * Initial support for gRPC stubs, async only for now. * New test for gRPC that uses stubs. Fixes a number of checkstyle and copyright issues. * Use WebClient to create GrpcClient(s). * Fixed unary blocking test. * Support for TLS. Switched tests to use TLS. * Simplified and optimized implementation of gRPC unary calls. * Moved test to webclient module. * Removed currently unused types from grpc core module. Signed-off-by: Santiago Pericasgeertsen * Cleanup of webserver gRPC module. Signed-off-by: Santiago Pericasgeertsen * Test cleanup and copyright errors. Signed-off-by: Santiago Pericasgeertsen * Adds support for setting up gRPC routes in tests. Updates gRPC tests to implement @SetupRoute methods. * Removes currently unused types. Signed-off-by: Santiago Pericasgeertsen * - Simplifies the grpc.core module to the bare minimum that is needed now - Cleans all module-info.java files - Switches to System.Logger for logging * Switches to socket context logging. Throws more detailed exception if baseUri is not present. * Converts PriorityBag to WeightedBag. Signed-off-by: Santiago Pericasgeertsen * Introduces support for client interceptors in low-level, stub-free API. * Basic support for client interceptors with service stubs * Improves support for empty server streams. Includes fix in Http2ClientStream to allow transitions from HEADERS to END. New tests. * Adds more tests for client and server empty streams. * Handles server EOS in HEADERS frame on the client side. Increased read timeout on client. * Some new tests from JK and fixes to gRPC client code to handle: (1) exceptions thrown in gRPC methods and (2) larger payloads. * Fixes problems in unary calls with large payloads. * Use protobuf version for protoc. Signed-off-by: Santiago Pericas-Geertsen * Basic support for gRPC protocol config settings * Javadoc updates. Signed-off-by: Santiago Pericas-Geertsen * Improves handling for server connection drops. On a read timeout, if the abort flag is not set, attempts to send a PING frame to verify the connection's health. * Uses a separate virtual thread to check if the connection to the server has died. The period to check for that is configurable and set to 5 seconds by default. * Sets heartbeat period to 0, thus disabling the feature by default. Signed-off-by: Santiago Pericas-Geertsen * Fixes logging message and test. * Refactors a few classes in grpc/core. Addresses other comments in PR review. * Reduces the number of logging statements in favor of using low-level HTTP/2 logging instead. Signed-off-by: Santiago Pericas-Geertsen * Some code reformatting using the Helidon scheme. Signed-off-by: Santiago Pericas-Geertsen --------- Signed-off-by: Santiago Pericasgeertsen Signed-off-by: Santiago Pericas-Geertsen --- all/pom.xml | 4 + bom/pom.xml | 16 + .../webserver/protocols/ProtocolsMain.java | 2 +- .../src/main/resources/logging.properties | 2 +- grpc/core/pom.xml | 69 +++ .../grpc/core/DefaultMarshallerSupplier.java | 45 ++ .../helidon/grpc/core/InterceptorWeights.java | 63 +++ .../helidon/grpc/core/MarshallerSupplier.java | 45 ++ .../io/helidon/grpc/core/MethodHandler.java | 193 ++++++++ .../grpc/core/ProtoMarshallerSupplier.java | 50 +++ .../io/helidon/grpc/core/WeightedBag.java | 193 ++++++++ .../io/helidon/grpc/core/package-info.java | 20 + grpc/core/src/main/java/module-info.java | 30 ++ .../io/helidon/grpc/core/WeightedBagTest.java | 161 +++++++ grpc/pom.xml | 53 +++ pom.xml | 1 + webclient/grpc/pom.xml | 159 +++++++ .../webclient/grpc/GrpcBaseClientCall.java | 248 ++++++++++ .../helidon/webclient/grpc/GrpcChannel.java | 58 +++ .../io/helidon/webclient/grpc/GrpcClient.java | 117 +++++ .../webclient/grpc/GrpcClientCall.java | 243 ++++++++++ .../grpc/GrpcClientConfigBlueprint.java | 38 ++ .../webclient/grpc/GrpcClientImpl.java | 64 +++ .../grpc/GrpcClientMethodDescriptor.java | 424 ++++++++++++++++++ .../GrpcClientProtocolConfigBlueprint.java | 95 ++++ .../webclient/grpc/GrpcClientStream.java | 37 ++ .../webclient/grpc/GrpcProtocolProvider.java | 57 +++ .../webclient/grpc/GrpcServiceClient.java | 124 +++++ .../webclient/grpc/GrpcServiceClientImpl.java | 200 +++++++++ .../grpc/GrpcServiceDescriptorBlueprint.java | 76 ++++ .../webclient/grpc/GrpcUnaryClientCall.java | 135 ++++++ .../helidon/webclient/grpc/package-info.java | 20 + webclient/grpc/src/main/java/module-info.java | 44 ++ .../helidon/webclient/http2/Http2Client.java | 8 +- .../http2/Http2ClientConnection.java | 46 +- .../webclient/http2/Http2ClientImpl.java | 7 +- .../webclient/http2/Http2ClientStream.java | 89 +++- .../webclient/http2/Http2ConnectionCache.java | 21 +- .../webclient/http2/Http2StreamConfig.java | 23 +- .../http2/LockingStreamIdSequence.java | 13 +- webclient/pom.xml | 1 + webclient/tests/grpc/pom.xml | 148 ++++++ .../tests/grpc/src/main/proto/events.proto | 60 +++ .../tests/grpc/src/main/proto/strings.proto | 32 ++ .../webclient/grpc/tests/GrpcBaseTest.java | 249 ++++++++++ .../grpc/tests/GrpcConfigProtocolTest.java | 52 +++ .../grpc/tests/GrpcConnectionErrorTest.java | 94 ++++ .../grpc/tests/GrpcInterceptorStubTest.java | 67 +++ .../grpc/tests/GrpcInterceptorTest.java | 79 ++++ .../webclient/grpc/tests/GrpcStubTest.java | 190 ++++++++ .../webclient/grpc/tests/GrpcTest.java | 190 ++++++++ .../grpc/src/test/resources/application.yaml | 21 + .../tests/grpc/src/test/resources/client.p12 | Bin 0 -> 4181 bytes .../src/test/resources/logging.properties | 24 + .../tests/grpc/src/test/resources/server.p12 | Bin 0 -> 4133 bytes webclient/tests/pom.xml | 1 + .../helidon/webclient/websocket/WsClient.java | 4 +- .../java/io/helidon/webserver/grpc/Grpc.java | 8 +- .../webserver/grpc/GrpcProtocolSelector.java | 4 +- .../helidon/webserver/grpc/GrpcService.java | 2 +- .../webserver/grpc/GrpcServiceRoute.java | 2 +- webserver/testing/junit5/grpc/pom.xml | 57 +++ .../junit5/grpc/GrpcServerExtension.java | 87 ++++ .../testing/junit5/grpc/package-info.java | 20 + .../grpc/src/main/java/module-info.java | 32 ++ webserver/testing/junit5/pom.xml | 1 + 66 files changed, 4674 insertions(+), 44 deletions(-) create mode 100644 grpc/core/pom.xml create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/DefaultMarshallerSupplier.java create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/InterceptorWeights.java create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/MarshallerSupplier.java create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/MethodHandler.java create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/ProtoMarshallerSupplier.java create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/WeightedBag.java create mode 100644 grpc/core/src/main/java/io/helidon/grpc/core/package-info.java create mode 100644 grpc/core/src/main/java/module-info.java create mode 100644 grpc/core/src/test/java/io/helidon/grpc/core/WeightedBagTest.java create mode 100644 grpc/pom.xml create mode 100644 webclient/grpc/pom.xml create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcBaseClientCall.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcChannel.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClient.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientCall.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientConfigBlueprint.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientImpl.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientMethodDescriptor.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientProtocolConfigBlueprint.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientStream.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcProtocolProvider.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClient.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClientImpl.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceDescriptorBlueprint.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcUnaryClientCall.java create mode 100644 webclient/grpc/src/main/java/io/helidon/webclient/grpc/package-info.java create mode 100644 webclient/grpc/src/main/java/module-info.java create mode 100644 webclient/tests/grpc/pom.xml create mode 100644 webclient/tests/grpc/src/main/proto/events.proto create mode 100644 webclient/tests/grpc/src/main/proto/strings.proto create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcBaseTest.java create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConfigProtocolTest.java create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConnectionErrorTest.java create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorStubTest.java create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorTest.java create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcStubTest.java create mode 100644 webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcTest.java create mode 100644 webclient/tests/grpc/src/test/resources/application.yaml create mode 100644 webclient/tests/grpc/src/test/resources/client.p12 create mode 100644 webclient/tests/grpc/src/test/resources/logging.properties create mode 100644 webclient/tests/grpc/src/test/resources/server.p12 create mode 100644 webserver/testing/junit5/grpc/pom.xml create mode 100644 webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/GrpcServerExtension.java create mode 100644 webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/package-info.java create mode 100644 webserver/testing/junit5/grpc/src/main/java/module-info.java diff --git a/all/pom.xml b/all/pom.xml index 12e171fdcd0..d949614f078 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -978,6 +978,10 @@ io.helidon.webclient helidon-webclient-websocket + + io.helidon.webclient + helidon-webclient-grpc + io.helidon.webclient helidon-webclient-sse diff --git a/bom/pom.xml b/bom/pom.xml index ffd88f9a7e4..21811b1ba92 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -94,6 +94,12 @@ helidon-microprofile-graphql-server ${helidon.version} + + + io.helidon.grpc + helidon-grpc-core + ${helidon.version} + io.helidon.integrations.micronaut @@ -1257,6 +1263,11 @@ helidon-webserver-testing-junit5-websocket ${helidon.version} + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5-grpc + ${helidon.version} + io.helidon.webclient helidon-webclient-api @@ -1282,6 +1293,11 @@ helidon-webclient-websocket ${helidon.version} + + io.helidon.webclient + helidon-webclient-grpc + ${helidon.version} + io.helidon.webclient helidon-webclient-sse diff --git a/examples/webserver/protocols/src/main/java/io/helidon/examples/webserver/protocols/ProtocolsMain.java b/examples/webserver/protocols/src/main/java/io/helidon/examples/webserver/protocols/ProtocolsMain.java index e8010bacc8f..07e7c5adfa6 100644 --- a/examples/webserver/protocols/src/main/java/io/helidon/examples/webserver/protocols/ProtocolsMain.java +++ b/examples/webserver/protocols/src/main/java/io/helidon/examples/webserver/protocols/ProtocolsMain.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/examples/webserver/protocols/src/main/resources/logging.properties b/examples/webserver/protocols/src/main/resources/logging.properties index d09df1098a3..161f6db0dde 100644 --- a/examples/webserver/protocols/src/main/resources/logging.properties +++ b/examples/webserver/protocols/src/main/resources/logging.properties @@ -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/grpc/core/pom.xml b/grpc/core/pom.xml new file mode 100644 index 00000000000..a1a059712c2 --- /dev/null +++ b/grpc/core/pom.xml @@ -0,0 +1,69 @@ + + + + + + helidon-grpc-project + io.helidon.grpc + 4.0.0-SNAPSHOT + + 4.0.0 + + helidon-grpc-core + Helidon gRPC related modules + + + + io.helidon.common + helidon-common + + + io.grpc + grpc-api + + + io.grpc + grpc-protobuf + + + com.google.protobuf + protobuf-java + + + io.grpc + grpc-stub + + + com.google.j2objc + j2objc-annotations + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/DefaultMarshallerSupplier.java b/grpc/core/src/main/java/io/helidon/grpc/core/DefaultMarshallerSupplier.java new file mode 100644 index 00000000000..8a3a0afe266 --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/DefaultMarshallerSupplier.java @@ -0,0 +1,45 @@ +/* + * 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.grpc.core; + +import com.google.protobuf.MessageLite; +import io.grpc.MethodDescriptor; + +/** + * The default {@link MarshallerSupplier}. + */ +class DefaultMarshallerSupplier implements MarshallerSupplier { + + private DefaultMarshallerSupplier() { + } + + static DefaultMarshallerSupplier create() { + return new DefaultMarshallerSupplier(); + } + + private final ProtoMarshallerSupplier proto = ProtoMarshallerSupplier.create(); + + @Override + public MethodDescriptor.Marshaller get(Class clazz) { + if (MessageLite.class.isAssignableFrom(clazz)) { + return proto.get(clazz); + } + String msg = String.format( + "Class %s must be a valid ProtoBuf message, or a custom marshaller for it must be specified explicitly", + clazz.getName()); + throw new IllegalArgumentException(msg); + } +} diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/InterceptorWeights.java b/grpc/core/src/main/java/io/helidon/grpc/core/InterceptorWeights.java new file mode 100644 index 00000000000..2975789be85 --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/InterceptorWeights.java @@ -0,0 +1,63 @@ +/* + * 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.grpc.core; + +/** + * gRPC interceptor weight classes. Higher weight means higher priority. + */ +public class InterceptorWeights { + + /** + * Context weight. + *

    + * Interceptors with this weight typically only perform tasks + * such as adding state to the call {@link io.grpc.Context}. + */ + public static final int CONTEXT = 5000; + + /** + * Tracing weight. + *

    + * Tracing and metrics interceptors are typically applied after any context + * interceptors so that they can trace and gather metrics on the whole call + * stack of remaining interceptors. + */ + public static final int TRACING = CONTEXT + 1; + + /** + * Security authentication weight. + */ + public static final int AUTHENTICATION = 2000; + + /** + * Security authorization weight. + */ + public static final int AUTHORIZATION = 2000; + + /** + * User-level weight. + *

    + * This value is also used as a default weight for application-supplied interceptors. + */ + public static final int USER = 1000; + + /** + * Cannot create instances. + */ + private InterceptorWeights() { + } +} diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/MarshallerSupplier.java b/grpc/core/src/main/java/io/helidon/grpc/core/MarshallerSupplier.java new file mode 100644 index 00000000000..7a2d3ba2bcb --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/MarshallerSupplier.java @@ -0,0 +1,45 @@ +/* + * 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. + * 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.grpc.core; + +import io.grpc.MethodDescriptor; + +/** + * A supplier of {@link MethodDescriptor.Marshaller} instances for specific + * classes. + */ +@FunctionalInterface +public interface MarshallerSupplier { + + /** + * Obtain a {@link MethodDescriptor.Marshaller} for a type. + * + * @param clazz the {@link Class} of the type to obtain the {@link MethodDescriptor.Marshaller} for + * @param the type to be marshalled + * @return a {@link MethodDescriptor.Marshaller} for a type + */ + MethodDescriptor.Marshaller get(Class clazz); + + /** + * Creates a default marshaller supplier. + * + * @return the default marshaller supplier + */ + static MarshallerSupplier create() { + return DefaultMarshallerSupplier.create(); + } +} diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/MethodHandler.java b/grpc/core/src/main/java/io/helidon/grpc/core/MethodHandler.java new file mode 100644 index 00000000000..6c0d238c719 --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/MethodHandler.java @@ -0,0 +1,193 @@ +/* + * 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. + * 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.grpc.core; + +import java.util.concurrent.CompletionStage; + +import io.grpc.MethodDescriptor; +import io.grpc.Status; +import io.grpc.stub.ServerCalls; +import io.grpc.stub.StreamObserver; + +/** + * A gRPC method call handler. + * + * @param the request type + * @param the response type + */ +public interface MethodHandler + extends ServerCalls.UnaryMethod, + ServerCalls.ClientStreamingMethod, + ServerCalls.ServerStreamingMethod, + ServerCalls.BidiStreamingMethod { + /** + * Obtain the {@link MethodDescriptor.MethodType gRPC method tyoe} that + * this {@link MethodHandler} handles. + * + * @return the {@link MethodDescriptor.MethodType gRPC method type} that + * this {@link MethodHandler} handles + */ + MethodDescriptor.MethodType type(); + + /** + * Obtain the request type. + * + * @return the request type + */ + Class getRequestType(); + + /** + * Obtain the response type. + * + * @return the response type + */ + Class getResponseType(); + + /** + * Obtain the name of the underlying Java method that this handler maps to. + * + * @return the name of the underlying Java method that this handler maps to + */ + String javaMethodName(); + + /** + * Determine whether this is a client side only handler. + * + * @return {@code true} if this handler can only be used on the client + */ + default boolean clientOnly() { + return false; + } + + @Override + default void invoke(ReqT request, StreamObserver observer) { + observer.onError(Status.UNIMPLEMENTED.asException()); + } + + @Override + default StreamObserver invoke(StreamObserver observer) { + observer.onError(Status.UNIMPLEMENTED.asException()); + return null; + } + + /** + * Handle a bi-directional client call. + * + * @param args the call arguments. + * @param client the {@link BidirectionalClient} instance to forward the call to. + * @return the call result + */ + default Object bidirectional(Object[] args, BidirectionalClient client) { + throw Status.UNIMPLEMENTED.asRuntimeException(); + } + + /** + * Handle a bi-directional client call. + * + * @param args the call arguments. + * @param client the {@link ClientStreaming} instance to forward the call to. + * @return the call result + */ + default Object clientStreaming(Object[] args, ClientStreaming client) { + throw Status.UNIMPLEMENTED.asRuntimeException(); + } + + /** + * Handle a bi-directional client call. + * + * @param args the call arguments. + * @param client the {@link ServerStreamingClient} instance to forward the call to. + * @return the call result + */ + default Object serverStreaming(Object[] args, ServerStreamingClient client) { + throw Status.UNIMPLEMENTED.asRuntimeException(); + } + + /** + * Handle a bi-directional client call. + * + * @param args the call arguments. + * @param client the {@link UnaryClient} instance to forward the call to. + * @return the call result + */ + default Object unary(Object[] args, UnaryClient client) { + throw Status.UNIMPLEMENTED.asRuntimeException(); + } + + /** + * A bidirectional client call handler. + */ + interface BidirectionalClient { + /** + * Perform a bidirectional client call. + * + * @param methodName the name of the gRPC method + * @param observer the {@link StreamObserver} that will receive the responses + * @param the request type + * @param the response type + * @return a {@link StreamObserver} to use to send requests + */ + StreamObserver bidiStreaming(String methodName, StreamObserver observer); + } + + /** + * A client streaming client call handler. + */ + interface ClientStreaming { + /** + * Perform a client streaming client call. + * + * @param methodName the name of the gRPC method + * @param observer the {@link StreamObserver} that will receive the responses + * @param the request type + * @param the response type + * @return a {@link StreamObserver} to use to send requests + */ + StreamObserver clientStreaming(String methodName, StreamObserver observer); + } + + /** + * A server streaming client call handler. + */ + interface ServerStreamingClient { + /** + * Perform a server streaming client call. + * + * @param methodName the name of the gRPC method + * @param request the request message + * @param observer the {@link StreamObserver} that will receive the responses + * @param the request type + * @param the response type + */ + void serverStreaming(String methodName, ReqT request, StreamObserver observer); + } + + /** + * A unary client call handler. + */ + interface UnaryClient { + /** + * Perform a unary client call. + * + * @param methodName the name of the gRPC method + * @param request the request message + * @param the request type + * @param the response type + * @return a {@link java.util.concurrent.CompletableFuture} that completes when the call completes + */ + CompletionStage unary(String methodName, ReqT request); + } +} diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/ProtoMarshallerSupplier.java b/grpc/core/src/main/java/io/helidon/grpc/core/ProtoMarshallerSupplier.java new file mode 100644 index 00000000000..6dff325413b --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/ProtoMarshallerSupplier.java @@ -0,0 +1,50 @@ +/* + * 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.grpc.core; + +import com.google.protobuf.Message; +import io.grpc.MethodDescriptor; +import io.grpc.protobuf.ProtoUtils; + +/** + * A {@link MarshallerSupplier} implementation that supplies Protocol Buffer + * marshaller instances. + */ +class ProtoMarshallerSupplier implements MarshallerSupplier { + + private ProtoMarshallerSupplier() { + } + + static ProtoMarshallerSupplier create() { + return new ProtoMarshallerSupplier(); + } + + @Override + @SuppressWarnings("unchecked") + public MethodDescriptor.Marshaller get(Class clazz) { + try { + java.lang.reflect.Method getDefaultInstance = clazz.getDeclaredMethod("getDefaultInstance"); + Message instance = (Message) getDefaultInstance.invoke(clazz); + return (MethodDescriptor.Marshaller) ProtoUtils.marshaller(instance); + } catch (Exception e) { + String msg = String.format( + "Attempting to use class %s, which is not a valid Protocol buffer message, with a default marshaller", + clazz.getName()); + throw new IllegalArgumentException(msg); + } + } +} + diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/WeightedBag.java b/grpc/core/src/main/java/io/helidon/grpc/core/WeightedBag.java new file mode 100644 index 00000000000..6199ab63ff8 --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/WeightedBag.java @@ -0,0 +1,193 @@ +/* + * 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. + * 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.grpc.core; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Stream; + +import io.helidon.common.Weight; +import io.helidon.common.Weighted; + +/** + * A bag of values ordered by weight. All weights must be greater or equal to 0. + *

    + * The higher the weight the higher the priority. For cases where the weight is the same, + * elements are returned in the order that they were added to the bag. + * + * @param the type of elements in the bag + * @see io.helidon.common.Weight + */ +public class WeightedBag implements Iterable { + + private final Map> contents; + private final double defaultWeight; + + private WeightedBag(Map> contents, double defaultWeight) { + this.contents = contents; + if (defaultWeight < 0.0) { + throw new IllegalArgumentException("Weights must be greater or equal to 0"); + } + this.defaultWeight = defaultWeight; + } + + /** + * Create a new {@link WeightedBag} where elements added with no weight will be + * given a default weight of 0. + * + * @param the type of elements in the bag + * @return a new {@link WeightedBag} where elements added + * with no weight will be given {@link Weighted#DEFAULT_WEIGHT} + */ + public static WeightedBag create() { + return create(Weighted.DEFAULT_WEIGHT); + } + + /** + * Create a new {@link WeightedBag} where elements added with no weight will be + * given a default weight of 0. + * + * @param defaultWeight default weight for elements + * @param the type of elements in the bag + * @return a new {@link WeightedBag} where elements added + * with no weight will be given {@link Weighted#DEFAULT_WEIGHT} + */ + public static WeightedBag create(double defaultWeight) { + return new WeightedBag<>(new TreeMap<>( + (o1, o2) -> Double.compare(o2, o1)), // reversed for weights + defaultWeight); + } + + /** + * Check if bag is empty. + * + * @return outcome of test + */ + public boolean isEmpty() { + return contents.isEmpty(); + } + + /** + * Obtain an immutable copy of this {@link WeightedBag}. + * + * @return an immutable copy of this {@link WeightedBag} + */ + public WeightedBag readOnly() { + return new WeightedBag<>(Collections.unmodifiableMap(contents), defaultWeight); + } + + /** + * Merge a {@link WeightedBag} into this {@link WeightedBag}. + * + * @param bag the bag to merge + */ + public void merge(WeightedBag bag) { + bag.contents.forEach((weight, value) -> addAll(value, weight)); + } + + /** + * Add elements to the bag. + *

    + * If the element's class is annotated with the {@link io.helidon.common.Weight} + * annotation then that value will be used to determine weight otherwise the + * default weight value will be used. + * + * @param values the elements to add + */ + public void addAll(Iterable values) { + for (T value : values) { + add(value); + } + } + + /** + * Add elements to the bag with a given weight. + * + * @param values the elements to add + * @param weight the weight to assign to the elements + */ + public void addAll(Iterable values, double weight) { + for (T value : values) { + add(value, weight); + } + } + + /** + * Add an element to the bag. + *

    + * If the element's class is annotated with the {@link io.helidon.common.Weight} + * annotation then that value will be used to determine weight otherwise the + * default weight value will be used. + * + * @param value the element to add + */ + public void add(T value) { + Objects.requireNonNull(value); + double weight; + if (value instanceof Weighted weighted) { + weight = weighted.weight(); + } else { + Weight annotation = value.getClass().getAnnotation(Weight.class); + weight = (annotation == null) ? defaultWeight : annotation.value(); + } + add(value, weight); + } + + /** + * Add an element to the bag with a specific weight. + * + * @param value the element to add + * @param weight the weight of the element + */ + public void add(T value, double weight) { + Objects.requireNonNull(value); + if (weight < 0.0) { + throw new IllegalArgumentException("Weights must be greater or equal to 0"); + } + contents.compute(weight, (key, list) -> { + List newList = list; + if (newList == null) { + newList = new ArrayList<>(); + } + newList.add(value); + return newList; + }); + } + + /** + * Obtain the contents of this {@link WeightedBag} as + * an ordered {@link Stream}. + * + * @return the contents of this {@link WeightedBag} as + * an ordered {@link Stream} + */ + public Stream stream() { + return contents.entrySet() + .stream() + .flatMap(e -> e.getValue().stream()); + } + + @Override + public Iterator iterator() { + return stream().iterator(); + } +} diff --git a/grpc/core/src/main/java/io/helidon/grpc/core/package-info.java b/grpc/core/src/main/java/io/helidon/grpc/core/package-info.java new file mode 100644 index 00000000000..a10d014bfed --- /dev/null +++ b/grpc/core/src/main/java/io/helidon/grpc/core/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. + */ + +/** + * Core classes used by both the gRPC server API and gRPC client API. + */ +package io.helidon.grpc.core; diff --git a/grpc/core/src/main/java/module-info.java b/grpc/core/src/main/java/module-info.java new file mode 100644 index 00000000000..695cf325613 --- /dev/null +++ b/grpc/core/src/main/java/module-info.java @@ -0,0 +1,30 @@ +/* + * 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. + * 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 GRPC core package. + */ +module io.helidon.grpc.core { + + requires io.helidon.common; + requires transitive io.grpc; + requires transitive io.grpc.stub; + requires transitive com.google.protobuf; + requires transitive io.grpc.protobuf; + requires transitive io.grpc.protobuf.lite; + + exports io.helidon.grpc.core; +} diff --git a/grpc/core/src/test/java/io/helidon/grpc/core/WeightedBagTest.java b/grpc/core/src/test/java/io/helidon/grpc/core/WeightedBagTest.java new file mode 100644 index 00000000000..5000a3065c9 --- /dev/null +++ b/grpc/core/src/test/java/io/helidon/grpc/core/WeightedBagTest.java @@ -0,0 +1,161 @@ +/* + * 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. + * 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.grpc.core; + +import java.util.Arrays; + +import io.helidon.common.Weight; +import io.helidon.common.Weighted; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class WeightedBagTest { + + @Test + void shouldReturnElementsInOrder() { + WeightedBag bag = WeightedBag.create(); + bag.add("Three", 3.0); + bag.add("Two", 2.0); + bag.add("One", 1.0); + assertThat(bag, contains("Three", "Two", "One")); + } + + @Test + void shouldReturnElementsInOrderWithinSameWeight() { + WeightedBag bag = WeightedBag.create(); + bag.add("Two", 2.0); + bag.add("TwoToo", 2.0); + assertThat(bag, contains("Two", "TwoToo")); + } + + @Test + void shouldReturnNoWeightElementsLast() { + WeightedBag bag = WeightedBag.create(); + bag.add("Three", 300.0); + bag.add("Last"); // default weight 100 + bag.add("One", 200.0); + assertThat(bag, contains("Three", "One", "Last")); + } + + @Test + void shouldGetWeightFromAnnotation() { + WeightedBag bag = WeightedBag.create(); + Value value = new Value(); + bag.add("One", 1.0); + bag.add("Three", 3.0); + bag.add(value); + assertThat(bag, contains("Three", value, "One")); + } + + @Test + void shouldGetWeightFromWeighted() { + WeightedBag bag = WeightedBag.create(); + WeightedValue value = new WeightedValue(); + bag.add("One", 1.0); + bag.add("Three", 3.0); + bag.add(value); + assertThat(bag, contains("Three", value, "One")); + } + + @Test + void shouldUseWeightFromWeightedOverAnnotation() { + WeightedBag bag = WeightedBag.create(); + AnnotatedWeightedValue value = new AnnotatedWeightedValue(); + bag.add("One", 1.0); + bag.add("Three", 3.0); + bag.add(value); + assertThat(bag, contains("Three", value, "One")); + } + + @Test + void shouldUseDefaultWeight() { + WeightedBag bag = WeightedBag.create(2.0); + bag.add("One", 1.0); + bag.add("Three", 3.0); + bag.add("Two"); + assertThat(bag, contains("Three", "Two", "One")); + } + + @Test + void shouldAddAll() { + WeightedBag bag = WeightedBag.create(); + bag.addAll(Arrays.asList("Three", "Two", "One")); + assertThat(bag, contains("Three", "Two", "One")); + } + + @Test + void shouldAddAllWithWeight() { + WeightedBag bag = WeightedBag.create(); + bag.add("First", 1.0); + bag.add("Last", 3.0); + bag.addAll(Arrays.asList("Three", "Two", "One"), 2.0); + assertThat(bag, contains("Last", "Three", "Two", "One", "First")); + } + + @Test + void shouldMerge() { + WeightedBag bagOne = WeightedBag.create(); + WeightedBag bagTwo = WeightedBag.create(); + + bagOne.add("A", 1.0); + bagOne.add("B", 2.0); + bagOne.add("C", 2.0); + bagOne.add("D", 3.0); + + bagTwo.add("E", 1.0); + bagTwo.add("F", 3.0); + bagTwo.add("G", 3.0); + bagTwo.add("H", 4.0); + + bagOne.merge(bagTwo); + assertThat(bagOne, contains("H", "D", "F", "G", "B", "C", "A", "E")); + } + + @Test + void badValue() { + WeightedBag bag = WeightedBag.create(); + assertThrows(NullPointerException.class, () -> bag.add(null, 1.0)); + } + + @Test + void badWeight() { + WeightedBag bag = WeightedBag.create(); + assertThrows(IllegalArgumentException.class, () -> bag.add("First", -1.0)); + } + + @Weight(2.0) + public static class Value { + } + + public static class WeightedValue implements Weighted { + @Override + public double weight() { + return 2.0; + } + } + + @Weight(0.0) + public static class AnnotatedWeightedValue implements Weighted { + @Override + public double weight() { + return 2.0; + } + } +} diff --git a/grpc/pom.xml b/grpc/pom.xml new file mode 100644 index 00000000000..2bbfdce4d6e --- /dev/null +++ b/grpc/pom.xml @@ -0,0 +1,53 @@ + + + + + 4.0.0 + + io.helidon + helidon-project + 4.0.0-SNAPSHOT + + pom + + io.helidon.grpc + helidon-grpc-project + Helidon gRPC Project + + gRPC support for Helidon + + + core + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + check-dependencies + verify + + + + + + diff --git a/pom.xml b/pom.xml index db3d6bfeb05..bc27020a09a 100644 --- a/pom.xml +++ b/pom.xml @@ -203,6 +203,7 @@ dependencies fault-tolerance graphql + grpc health helidon http diff --git a/webclient/grpc/pom.xml b/webclient/grpc/pom.xml new file mode 100644 index 00000000000..e86fc5ed478 --- /dev/null +++ b/webclient/grpc/pom.xml @@ -0,0 +1,159 @@ + + + + 4.0.0 + + io.helidon.webclient + helidon-webclient-project + 4.0.0-SNAPSHOT + + + helidon-webclient-grpc + Helidon WebClient gRPC + + + + io.grpc + grpc-core + + + io.grpc + grpc-stub + + + io.helidon.grpc + helidon-grpc-core + + + io.helidon.http + helidon-http-http2 + + + io.helidon.http.encoding + helidon-http-encoding + + + io.helidon.webclient + helidon-webclient + + + io.helidon.webclient + helidon-webclient-http2 + + + io.helidon.common.features + helidon-common-features-api + true + + + io.helidon.config + helidon-config-yaml + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + io.helidon.webserver + helidon-webserver-grpc + test + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5 + test + + + org.junit.jupiter + junit-jupiter-params + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.helidon.common.features + helidon-common-features-processor + ${helidon.version} + + + io.helidon.config + helidon-config-metadata-processor + ${helidon.version} + + + io.helidon.builder + helidon-builder-processor + ${helidon.version} + + + io.helidon.common.processor + helidon-common-processor-helidon-copyright + ${helidon.version} + + + + + + io.helidon.common.features + helidon-common-features-processor + ${helidon.version} + + + io.helidon.config + helidon-config-metadata-processor + ${helidon.version} + + + io.helidon.builder + helidon-builder-processor + ${helidon.version} + + + io.helidon.common.processor + helidon-common-processor-helidon-copyright + ${helidon.version} + + + + + io.helidon.build-tools + helidon-services-plugin + ${version.plugin.helidon-build-tools} + + fail + + + + + + diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcBaseClientCall.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcBaseClientCall.java new file mode 100644 index 00000000000..65ebfced73d --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcBaseClientCall.java @@ -0,0 +1,248 @@ +/* + * 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.webclient.grpc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.time.Duration; +import java.util.Collections; +import java.util.concurrent.Executor; + +import io.helidon.common.buffers.BufferData; +import io.helidon.common.socket.HelidonSocket; +import io.helidon.http.Header; +import io.helidon.http.HeaderNames; +import io.helidon.http.HeaderValues; +import io.helidon.http.WritableHeaders; +import io.helidon.http.http2.Http2Headers; +import io.helidon.http.http2.Http2Settings; +import io.helidon.http.http2.Http2StreamState; +import io.helidon.webclient.api.ClientConnection; +import io.helidon.webclient.api.ClientUri; +import io.helidon.webclient.api.ConnectionKey; +import io.helidon.webclient.api.DefaultDnsResolver; +import io.helidon.webclient.api.DnsAddressLookup; +import io.helidon.webclient.api.Proxy; +import io.helidon.webclient.api.TcpClientConnection; +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.http2.Http2ClientConnection; +import io.helidon.webclient.http2.Http2ClientImpl; +import io.helidon.webclient.http2.Http2StreamConfig; + +import io.grpc.CallOptions; +import io.grpc.ClientCall; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; + +import static java.lang.System.Logger.Level.DEBUG; + +/** + * Base class for gRPC client calls. + */ +abstract class GrpcBaseClientCall extends ClientCall { + private static final System.Logger LOGGER = System.getLogger(GrpcBaseClientCall.class.getName()); + + protected static final Metadata EMPTY_METADATA = new Metadata(); + protected static final Header GRPC_ACCEPT_ENCODING = HeaderValues.create(HeaderNames.ACCEPT_ENCODING, "gzip"); + protected static final Header GRPC_CONTENT_TYPE = HeaderValues.create(HeaderNames.CONTENT_TYPE, "application/grpc"); + + protected static final BufferData PING_FRAME = BufferData.create("PING"); + protected static final BufferData EMPTY_BUFFER_DATA = BufferData.empty(); + + private final GrpcClientImpl grpcClient; + private final MethodDescriptor methodDescriptor; + private final CallOptions callOptions; + private final int initBufferSize; + private final Duration pollWaitTime; + private final boolean abortPollTimeExpired; + private final Duration heartbeatPeriod; + + private final MethodDescriptor.Marshaller requestMarshaller; + private final MethodDescriptor.Marshaller responseMarshaller; + + private volatile Http2ClientConnection connection; + private volatile GrpcClientStream clientStream; + private volatile Listener responseListener; + private volatile HelidonSocket socket; + + GrpcBaseClientCall(GrpcClientImpl grpcClient, MethodDescriptor methodDescriptor, CallOptions callOptions) { + this.grpcClient = grpcClient; + this.methodDescriptor = methodDescriptor; + this.callOptions = callOptions; + this.requestMarshaller = methodDescriptor.getRequestMarshaller(); + this.responseMarshaller = methodDescriptor.getResponseMarshaller(); + this.initBufferSize = grpcClient.prototype().protocolConfig().initBufferSize(); + this.pollWaitTime = grpcClient.prototype().protocolConfig().pollWaitTime(); + this.abortPollTimeExpired = grpcClient.prototype().protocolConfig().abortPollTimeExpired(); + this.heartbeatPeriod = grpcClient.prototype().protocolConfig().heartbeatPeriod(); + } + + @Override + public void start(Listener responseListener, Metadata metadata) { + LOGGER.log(DEBUG, "start called"); + + this.responseListener = responseListener; + + // obtain HTTP2 connection + ClientConnection clientConnection = clientConnection(); + socket = clientConnection.helidonSocket(); + connection = Http2ClientConnection.create((Http2ClientImpl) grpcClient.http2Client(), + clientConnection, true); + + // create HTTP2 stream from connection + clientStream = new GrpcClientStream( + connection, + Http2Settings.create(), // Http2Settings + socket, // SocketContext + new Http2StreamConfig() { + @Override + public boolean priorKnowledge() { + return true; + } + + @Override + public int priority() { + return 0; + } + + @Override + public Duration readTimeout() { + return grpcClient.prototype().readTimeout().orElse( + grpcClient.prototype().protocolConfig().pollWaitTime()); + } + }, + null, // Http2ClientConfig + connection.streamIdSequence()); + + // start streaming threads + startStreamingThreads(); + + // send HEADERS frame + ClientUri clientUri = grpcClient.prototype().baseUri().orElseThrow(); + WritableHeaders headers = WritableHeaders.create(); + headers.add(Http2Headers.AUTHORITY_NAME, clientUri.authority()); + headers.add(Http2Headers.METHOD_NAME, "POST"); + headers.add(Http2Headers.PATH_NAME, "/" + methodDescriptor.getFullMethodName()); + headers.add(Http2Headers.SCHEME_NAME, "http"); + headers.add(GRPC_CONTENT_TYPE); + headers.add(GRPC_ACCEPT_ENCODING); + clientStream.writeHeaders(Http2Headers.create(headers), false); + } + + abstract void startStreamingThreads(); + + /** + * Unary blocking calls that use stubs provide their own executor which needs + * to be used at least once to unblock the calling thread and complete the + * gRPC invocation. This method submits an empty task for that purpose. There + * may be a better way to achieve this. + */ + protected void unblockUnaryExecutor() { + Executor executor = callOptions.getExecutor(); + if (executor != null) { + try { + executor.execute(() -> { + }); + } catch (Throwable t) { + // ignored + } + } + } + + protected ClientConnection clientConnection() { + GrpcClientConfig clientConfig = grpcClient.prototype(); + ClientUri clientUri = clientConfig.baseUri().orElseThrow(); + WebClient webClient = grpcClient.webClient(); + + ConnectionKey connectionKey = new ConnectionKey( + clientUri.scheme(), + clientUri.host(), + clientUri.port(), + clientConfig.readTimeout().orElse(Duration.ZERO), + clientConfig.tls(), + DefaultDnsResolver.create(), + DnsAddressLookup.defaultLookup(), + Proxy.noProxy()); + + return TcpClientConnection.create(webClient, + connectionKey, + Collections.emptyList(), + connection -> false, + connection -> { + }).connect(); + } + + protected boolean isRemoteOpen() { + return clientStream.streamState() != Http2StreamState.HALF_CLOSED_REMOTE + && clientStream.streamState() != Http2StreamState.CLOSED; + } + + protected ResT toResponse(BufferData bufferData) { + bufferData.read(); // compression + bufferData.readUnsignedInt32(); // length prefixed + return responseMarshaller.parse(new InputStream() { + @Override + public int read() { + return bufferData.available() > 0 ? bufferData.read() : -1; + } + }); + } + + protected byte[] serializeMessage(ReqT message) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(initBufferSize); + try (InputStream is = requestMarshaller().stream(message)) { + is.transferTo(baos); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return baos.toByteArray(); + } + + protected Duration heartbeatPeriod() { + return heartbeatPeriod; + } + + protected boolean abortPollTimeExpired() { + return abortPollTimeExpired; + } + + protected Duration pollWaitTime() { + return pollWaitTime; + } + + protected Http2ClientConnection connection() { + return connection; + } + + protected MethodDescriptor.Marshaller requestMarshaller() { + return requestMarshaller; + } + + protected GrpcClientStream clientStream() { + return clientStream; + } + + protected Listener responseListener() { + return responseListener; + } + + protected HelidonSocket socket() { + return socket; + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcChannel.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcChannel.java new file mode 100644 index 00000000000..3efb29e47f7 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcChannel.java @@ -0,0 +1,58 @@ +/* + * 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.webclient.grpc; + +import io.helidon.webclient.api.ClientUri; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.MethodDescriptor; + +/** + * Helidon's implementation of a gRPC {@link Channel}. + */ +class GrpcChannel extends Channel { + + private final GrpcClientImpl grpcClient; + + /** + * Creates a new channel from a {@link GrpcClient}. + * + * @param grpcClient the gRPC client + */ + GrpcChannel(GrpcClient grpcClient) { + this.grpcClient = (GrpcClientImpl) grpcClient; + } + + @Override + public ClientCall newCall( + MethodDescriptor methodDescriptor, CallOptions callOptions) { + MethodDescriptor.MethodType methodType = methodDescriptor.getType(); + return methodType == MethodDescriptor.MethodType.UNARY + ? new GrpcUnaryClientCall<>(grpcClient, methodDescriptor, callOptions) + : new GrpcClientCall<>(grpcClient, methodDescriptor, callOptions); + } + + @Override + public String authority() { + ClientUri clientUri = grpcClient.prototype() + .baseUri() + .orElseThrow(() -> new IllegalArgumentException("No base URI provided for GrpcClient")); + return clientUri.authority(); + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClient.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClient.java new file mode 100644 index 00000000000..d786f7840f0 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClient.java @@ -0,0 +1,117 @@ +/* + * 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.webclient.grpc; + +import java.util.Collection; +import java.util.function.Consumer; + +import io.helidon.builder.api.RuntimeType; +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.spi.Protocol; + +import io.grpc.Channel; +import io.grpc.ClientInterceptor; + +/** + * gRPC client. + */ +@RuntimeType.PrototypedBy(GrpcClientConfig.class) +public interface GrpcClient extends RuntimeType.Api { + /** + * Protocol ID constant for gRPC. + */ + String PROTOCOL_ID = "grpc"; + + /** + * Protocol to use to obtain an instance of gRPC specific client from + * {@link io.helidon.webclient.api.WebClient#client(io.helidon.webclient.spi.Protocol)}. + */ + Protocol PROTOCOL = GrpcProtocolProvider::new; + + /** + * A new fluent API builder to customize client setup. + * + * @return a new builder + */ + static GrpcClientConfig.Builder builder() { + return GrpcClientConfig.builder(); + } + + /** + * Create a new instance with custom configuration. + * + * @param clientConfig HTTP/2 client configuration + * @return a new HTTP/2 client + */ + static GrpcClient create(GrpcClientConfig clientConfig) { + return new GrpcClientImpl(WebClient.create(it -> it.from(clientConfig)), clientConfig); + } + + /** + * Create a new instance customizing its configuration. + * + * @param consumer HTTP/2 client configuration + * @return a new HTTP/2 client + */ + static GrpcClient create(Consumer consumer) { + return create(GrpcClientConfig.builder() + .update(consumer) + .buildPrototype()); + } + + /** + * Create a new instance with default configuration. + * + * @return a new HTTP/2 client + */ + static GrpcClient create() { + return create(GrpcClientConfig.create()); + } + + /** + * Create a client for a specific service. The client will be backed by the same HTTP/2 client. + * + * @param descriptor descriptor to use + * @return client for the provided descriptor + */ + GrpcServiceClient serviceClient(GrpcServiceDescriptor descriptor); + + /** + * Create a gRPC channel for this client that can be used to create stubs. + * + * @return a new gRPC channel + */ + Channel channel(); + + /** + * Create a gRPC channel for this client that can be used to create stubs. + * + * @param interceptors the array of client interceptors + * @return a new gRPC channel + */ + Channel channel(ClientInterceptor... interceptors); + + /** + * Create a gRPC channel for this client that can be used to create stubs. + * + * @param interceptors the list of client interceptors + * @return a new gRPC channel + */ + default Channel channel(Collection interceptors) { + return channel(interceptors.toArray(new ClientInterceptor[]{})); + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientCall.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientCall.java new file mode 100644 index 00000000000..96573554350 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientCall.java @@ -0,0 +1,243 @@ +/* + * 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.webclient.grpc; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import io.helidon.common.buffers.BufferData; +import io.helidon.http.http2.Http2FrameData; +import io.helidon.webclient.http2.StreamTimeoutException; + +import io.grpc.CallOptions; +import io.grpc.MethodDescriptor; +import io.grpc.Status; + +import static java.lang.System.Logger.Level.DEBUG; +import static java.lang.System.Logger.Level.ERROR; + +/** + * An implementation of a gRPC call. Expects: + *

    + * start (request | sendMessage)* (halfClose | cancel) + * + * @param request type + * @param response type + */ +class GrpcClientCall extends GrpcBaseClientCall { + private static final System.Logger LOGGER = System.getLogger(GrpcClientCall.class.getName()); + + private final ExecutorService executor; + private final AtomicInteger messageRequest = new AtomicInteger(); + + private final LinkedBlockingQueue sendingQueue = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue receivingQueue = new LinkedBlockingQueue<>(); + + private final CountDownLatch startReadBarrier = new CountDownLatch(1); + private final CountDownLatch startWriteBarrier = new CountDownLatch(1); + + private volatile Future readStreamFuture; + private volatile Future writeStreamFuture; + private volatile Future heartbeatFuture; + + GrpcClientCall(GrpcClientImpl grpcClient, MethodDescriptor methodDescriptor, CallOptions callOptions) { + super(grpcClient, methodDescriptor, callOptions); + this.executor = grpcClient.webClient().executor(); + } + + @Override + public void request(int numMessages) { + socket().log(LOGGER, DEBUG, "request called %d", numMessages); + messageRequest.addAndGet(numMessages); + startReadBarrier.countDown(); + } + + @Override + public void cancel(String message, Throwable cause) { + socket().log(LOGGER, DEBUG, "cancel called %s", message); + responseListener().onClose(Status.CANCELLED, EMPTY_METADATA); + readStreamFuture.cancel(true); + writeStreamFuture.cancel(true); + heartbeatFuture.cancel(true); + close(); + } + + @Override + public void halfClose() { + socket().log(LOGGER, DEBUG, "halfClose called"); + sendingQueue.add(EMPTY_BUFFER_DATA); // end marker + startWriteBarrier.countDown(); + } + + @Override + public void sendMessage(ReqT message) { + // serialize and queue message for writing + byte[] serialized = serializeMessage(message); + BufferData messageData = BufferData.createReadOnly(serialized, 0, serialized.length); + BufferData headerData = BufferData.create(5); + headerData.writeInt8(0); // no compression + headerData.writeUnsignedInt32(messageData.available()); // length prefixed + sendingQueue.add(BufferData.create(headerData, messageData)); + startWriteBarrier.countDown(); + } + + protected void startStreamingThreads() { + // heartbeat thread + Duration period = heartbeatPeriod(); + if (!period.isZero()) { + heartbeatFuture = executor.submit(() -> { + try { + startWriteBarrier.await(); + socket().log(LOGGER, DEBUG, "[Heartbeat thread] started with period " + period); + + while (isRemoteOpen()) { + Thread.sleep(period); + if (sendingQueue.isEmpty()) { + sendingQueue.add(PING_FRAME); + } + } + } catch (Throwable t) { + socket().log(LOGGER, DEBUG, "[Heartbeat thread] exception " + t.getMessage()); + } + }); + } else { + heartbeatFuture = CompletableFuture.completedFuture(null); + } + + // write streaming thread + writeStreamFuture = executor.submit(() -> { + try { + startWriteBarrier.await(); + socket().log(LOGGER, DEBUG, "[Writing thread] started"); + + boolean endOfStream = false; + while (isRemoteOpen()) { + socket().log(LOGGER, DEBUG, "[Writing thread] polling sending queue"); + BufferData bufferData = sendingQueue.poll(pollWaitTime().toMillis(), TimeUnit.MILLISECONDS); + if (bufferData != null) { + if (bufferData == PING_FRAME) { // ping frame + clientStream().sendPing(); + continue; + } + if (bufferData == EMPTY_BUFFER_DATA) { // end marker + if (!endOfStream) { + clientStream().writeData(EMPTY_BUFFER_DATA, true); + } + break; + } + endOfStream = (sendingQueue.peek() == EMPTY_BUFFER_DATA); + boolean lastEndOfStream = endOfStream; + socket().log(LOGGER, DEBUG, "[Writing thread] writing bufferData %b", lastEndOfStream); + clientStream().writeData(bufferData, endOfStream); + } + } + } catch (Throwable e) { + socket().log(LOGGER, ERROR, e.getMessage(), e); + Status errorStatus = Status.UNKNOWN.withDescription(e.getMessage()).withCause(e); + responseListener().onClose(errorStatus, EMPTY_METADATA); + } + socket().log(LOGGER, DEBUG, "[Writing thread] exiting"); + }); + + // read streaming thread + readStreamFuture = executor.submit(() -> { + try { + startReadBarrier.await(); + socket().log(LOGGER, DEBUG, "[Reading thread] started"); + + // read response headers + boolean headersRead = false; + do { + try { + clientStream().readHeaders(); + headersRead = true; + } catch (StreamTimeoutException e) { + handleStreamTimeout(e); + } + } while (!headersRead); + + // read data from stream + while (isRemoteOpen()) { + // drain queue + drainReceivingQueue(); + + // trailers or eos received? + if (clientStream().trailers().isDone() || !clientStream().hasEntity()) { + socket().log(LOGGER, DEBUG, "[Reading thread] trailers or eos received"); + break; + } + + // attempt to read and queue + Http2FrameData frameData; + try { + frameData = clientStream().readOne(pollWaitTime()); + } catch (StreamTimeoutException e) { + handleStreamTimeout(e); + continue; + } + if (frameData != null) { + receivingQueue.add(frameData.data()); + socket().log(LOGGER, DEBUG, "[Reading thread] adding bufferData to receiving queue"); + } + } + + socket().log(LOGGER, DEBUG, "[Reading thread] closing listener"); + responseListener().onClose(Status.OK, EMPTY_METADATA); + } catch (StreamTimeoutException e) { + responseListener().onClose(Status.DEADLINE_EXCEEDED, EMPTY_METADATA); + } catch (Throwable e) { + socket().log(LOGGER, ERROR, e.getMessage(), e); + Status errorStatus = Status.UNKNOWN.withDescription(e.getMessage()).withCause(e); + responseListener().onClose(errorStatus, EMPTY_METADATA); + } finally { + close(); + } + socket().log(LOGGER, DEBUG, "[Reading thread] exiting"); + }); + } + + private void close() { + socket().log(LOGGER, DEBUG, "closing client call"); + sendingQueue.clear(); + clientStream().cancel(); + connection().close(); + unblockUnaryExecutor(); + } + + private void drainReceivingQueue() { + socket().log(LOGGER, DEBUG, "[Reading thread] draining receiving queue"); + while (messageRequest.get() > 0 && !receivingQueue.isEmpty()) { + messageRequest.getAndDecrement(); + ResT res = toResponse(receivingQueue.remove()); + responseListener().onMessage(res); + } + } + + private void handleStreamTimeout(StreamTimeoutException e) { + if (abortPollTimeExpired()) { + socket().log(LOGGER, ERROR, "[Reading thread] HTTP/2 stream timeout, aborting"); + throw e; + } + socket().log(LOGGER, ERROR, "[Reading thread] HTTP/2 stream timeout, retrying"); + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientConfigBlueprint.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientConfigBlueprint.java new file mode 100644 index 00000000000..7403c76a0ad --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientConfigBlueprint.java @@ -0,0 +1,38 @@ +/* + * 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.webclient.grpc; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; +import io.helidon.webclient.api.HttpClientConfig; + +/** + * Configuration of a grpc client. + */ +@Prototype.Blueprint +@Prototype.Configured +interface GrpcClientConfigBlueprint extends HttpClientConfig, Prototype.Factory { + + /** + * gRPC specific configuration. + * + * @return protocol specific configuration + */ + @Option.Default("create()") + @Option.Configured + GrpcClientProtocolConfig protocolConfig(); +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientImpl.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientImpl.java new file mode 100644 index 00000000000..7a8ce6dfa91 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientImpl.java @@ -0,0 +1,64 @@ +/* + * 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.webclient.grpc; + +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.http2.Http2Client; + +import io.grpc.Channel; +import io.grpc.ClientInterceptor; +import io.grpc.ClientInterceptors; + +class GrpcClientImpl implements GrpcClient { + private final WebClient webClient; + private final Http2Client http2Client; + private final GrpcClientConfig clientConfig; + + GrpcClientImpl(WebClient webClient, GrpcClientConfig clientConfig) { + this.webClient = webClient; + this.http2Client = webClient.client(Http2Client.PROTOCOL); + this.clientConfig = clientConfig; + } + + WebClient webClient() { + return webClient; + } + + Http2Client http2Client() { + return http2Client; + } + + @Override + public GrpcClientConfig prototype() { + return clientConfig; + } + + @Override + public GrpcServiceClient serviceClient(GrpcServiceDescriptor descriptor) { + return new GrpcServiceClientImpl(descriptor, this); + } + + @Override + public Channel channel() { + return new GrpcChannel(this); + } + + @Override + public Channel channel(ClientInterceptor... interceptors) { + return ClientInterceptors.intercept(channel(), interceptors); + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientMethodDescriptor.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientMethodDescriptor.java new file mode 100644 index 00000000000..7585b6944cb --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientMethodDescriptor.java @@ -0,0 +1,424 @@ +/* + * 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.webclient.grpc; + +import java.util.Arrays; +import java.util.Objects; + +import io.helidon.grpc.core.InterceptorWeights; +import io.helidon.grpc.core.MarshallerSupplier; +import io.helidon.grpc.core.MethodHandler; +import io.helidon.grpc.core.WeightedBag; + +import io.grpc.CallCredentials; +import io.grpc.ClientInterceptor; +import io.grpc.MethodDescriptor; + +/** + * Encapsulates all metadata necessary to define a gRPC method. In addition to wrapping + * a {@link io.grpc.MethodDescriptor}, this class also holds the request and response + * types of the gRPC method. A {@link io.helidon.webclient.grpc.GrpcServiceDescriptor} + * can contain zero or more {@link io.grpc.MethodDescriptor}. + *

    + * An instance of ClientMethodDescriptor can be created either from an existing + * {@link io.grpc.MethodDescriptor} or from one of the factory methods + * {@link #bidirectional(String, String)}, {@link #clientStreaming(String, String)}, + * {@link #serverStreaming(String, String)} or {@link #unary(String, String)}. + */ +public final class GrpcClientMethodDescriptor { + + /** + * The simple name of the method. + */ + private final String name; + + /** + * The {@link io.grpc.MethodDescriptor} for this method. This is usually obtained from + * protocol buffer method getDescriptor (from service getDescriptor). + */ + private final MethodDescriptor descriptor; + + /** + * The list of client interceptors for this method. + */ + private WeightedBag interceptors; + + /** + * The {@link io.grpc.CallCredentials} for this method. + */ + private final CallCredentials callCredentials; + + /** + * The method handler for this method. + */ + private final MethodHandler methodHandler; + + private GrpcClientMethodDescriptor(String name, + MethodDescriptor descriptor, + WeightedBag interceptors, + CallCredentials callCredentials, + MethodHandler methodHandler) { + this.name = name; + this.descriptor = descriptor; + this.interceptors = interceptors; + this.callCredentials = callCredentials; + this.methodHandler = methodHandler; + } + + /** + * Creates a new {@link GrpcClientMethodDescriptor.Builder} with the + * specified name and {@link io.grpc.MethodDescriptor}. + * + * @param serviceName the name of the owning gRPC service + * @param name the simple method name + * @param descriptor the underlying gRPC {@link io.grpc.MethodDescriptor.Builder} + * @return A new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static Builder builder(String serviceName, + String name, + MethodDescriptor.Builder descriptor) { + return new Builder(serviceName, name, descriptor); + } + + /** + * Creates a new {@link GrpcClientMethodDescriptor.Builder} with the + * specified name and {@link io.grpc.MethodDescriptor}. + * + * @param serviceName the name of the owning gRPC service + * @param name the simple method name + * @param descriptor the underlying gRPC {@link io.grpc.MethodDescriptor.Builder} + * @return a new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static GrpcClientMethodDescriptor create(String serviceName, + String name, + MethodDescriptor.Builder descriptor) { + return builder(serviceName, name, descriptor).build(); + } + + /** + * Creates a new unary {@link GrpcClientMethodDescriptor.Builder} with + * the specified name. + * + * @param serviceName the name of the owning gRPC service + * @param name the method name + * @return a new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static Builder unary(String serviceName, String name) { + return builder(serviceName, name, MethodDescriptor.MethodType.UNARY); + } + + /** + * Creates a new client Streaming {@link GrpcClientMethodDescriptor.Builder} with + * the specified name. + * + * @param serviceName the name of the owning gRPC service + * @param name the method name + * @return a new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static Builder clientStreaming(String serviceName, String name) { + return builder(serviceName, name, MethodDescriptor.MethodType.CLIENT_STREAMING); + } + + /** + * Creates a new server streaming {@link GrpcClientMethodDescriptor.Builder} with + * the specified name. + * + * @param serviceName the name of the owning gRPC service + * @param name the method name + * @return a new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static Builder serverStreaming(String serviceName, String name) { + return builder(serviceName, name, MethodDescriptor.MethodType.SERVER_STREAMING); + } + + /** + * Creates a new bidirectional {@link GrpcClientMethodDescriptor.Builder} with + * the specified name. + * + * @param serviceName the name of the owning gRPC service + * @param name the method name + * @return a new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static Builder bidirectional(String serviceName, String name) { + return builder(serviceName, name, MethodDescriptor.MethodType.BIDI_STREAMING); + } + + /** + * Obtain the {@link ClientInterceptor}s to use for this method. + * + * @return the {@link ClientInterceptor}s to use for this method + */ + WeightedBag interceptors() { + return interceptors.readOnly(); + } + + /** + * Return the {@link io.grpc.CallCredentials} set on this service. + * + * @return the {@link io.grpc.CallCredentials} set on this service + */ + public CallCredentials callCredentials() { + return this.callCredentials; + } + + /** + * Creates a new {@link GrpcClientMethodDescriptor.Builder} with the specified name. + * + * @param serviceName the name of the owning gRPC service + * @param name the method name + * @param methodType the gRPC method type + * @return a new instance of a {@link GrpcClientMethodDescriptor.Builder} + */ + public static Builder builder(String serviceName, String name, MethodDescriptor.MethodType methodType) { + MethodDescriptor.Builder builder = MethodDescriptor.newBuilder() + .setFullMethodName(serviceName + "/" + name) + .setType(methodType); + return new Builder(serviceName, name, builder) + .requestType(Object.class) + .responseType(Object.class); + } + + /** + * Returns the simple name of the method. + * + * @return The simple name of the method. + */ + public String name() { + return name; + } + + /** + * Returns the {@link io.grpc.MethodDescriptor} of this method. + * + * @param the request type + * @param the response type + * @return The {@link io.grpc.MethodDescriptor} of this method. + */ + @SuppressWarnings("unchecked") + public MethodDescriptor descriptor() { + return (MethodDescriptor) descriptor; + } + + /** + * Returns the {@link MethodDescriptor.MethodType} of this method. + * + * @return the method type + */ + public MethodDescriptor.MethodType type() { + return descriptor.getType(); + } + + /** + * Obtain the {@link MethodHandler} to use to make client calls. + * + * @return the {@link MethodHandler} to use to make client calls + */ + public MethodHandler methodHandler() { + return methodHandler; + } + + /** + * ClientMethod configuration API. + */ + public interface Rules { + + /** + * Sets the type of parameter of this method. + * + * @param type The type of parameter of this method. + * @return this {@link GrpcClientMethodDescriptor.Rules} instance for + * fluent call chaining + */ + Rules requestType(Class type); + + /** + * Sets the type of parameter of this method. + * + * @param type The type of parameter of this method. + * @return this {@link GrpcClientMethodDescriptor.Rules} instance for + * fluent call chaining + */ + Rules responseType(Class type); + + /** + * Register one or more {@link ClientInterceptor interceptors} for the method. + * + * @param interceptors the interceptor(s) to register + * @return this {@link Rules} instance for fluent call chaining + */ + Rules intercept(ClientInterceptor... interceptors); + + /** + * Register one or more {@link ClientInterceptor interceptors} for the method. + *

    + * The added interceptors will be applied using the specified priority. + * + * @param weight the weight to assign to the interceptors + * @param interceptors one or more {@link ClientInterceptor}s to register + * @return this {@link Rules} to allow fluent method chaining + */ + Rules intercept(double weight, ClientInterceptor... interceptors); + + /** + * Register the {@link MarshallerSupplier} for the method. + *

    + * If not set the default {@link MarshallerSupplier} from the service will be used. + * + * @param marshallerSupplier the {@link MarshallerSupplier} for the service + * @return this {@link GrpcClientMethodDescriptor.Rules} instance + * for fluent call chaining + */ + Rules marshallerSupplier(MarshallerSupplier marshallerSupplier); + + /** + * Register the specified {@link io.grpc.CallCredentials} to be used for this method. + * This overrides any {@link io.grpc.CallCredentials} set on the + * {@link GrpcClientMethodDescriptor}. + * + * @param callCredentials the {@link io.grpc.CallCredentials} to set. + * @return this {@link GrpcClientMethodDescriptor.Rules} instance + * for fluent call chaining + */ + Rules callCredentials(CallCredentials callCredentials); + + /** + * Set the {@link MethodHandler} that can be used to invoke the method. + * + * @param methodHandler the {2link MethodHandler} to use + * @return this {@link GrpcClientMethodDescriptor.Rules} instance + * for fluent call chaining + */ + Rules methodHandler(MethodHandler methodHandler); + } + + /** + * {@link io.grpc.MethodDescriptor} builder implementation. + */ + public static class Builder + implements Rules, io.helidon.common.Builder { + + private String name; + private final MethodDescriptor.Builder descriptor; + private Class requestType; + private Class responseType; + private final WeightedBag interceptors = WeightedBag.create(InterceptorWeights.USER); + private MarshallerSupplier defaultMarshallerSupplier = MarshallerSupplier.create(); + private MarshallerSupplier marshallerSupplier; + private CallCredentials callCredentials; + private MethodHandler methodHandler; + + /** + * Constructs a new Builder instance. + * + * @param serviceName The name of the service ths method belongs to + * @param name the name of this method + * @param descriptor The gRPC method descriptor builder + */ + Builder(String serviceName, String name, MethodDescriptor.Builder descriptor) { + this.name = name; + this.descriptor = descriptor.setFullMethodName(serviceName + "/" + name); + } + + @Override + public Builder requestType(Class type) { + this.requestType = type; + return this; + } + + @Override + public Builder responseType(Class type) { + this.responseType = type; + return this; + } + + @Override + public Builder intercept(ClientInterceptor... interceptors) { + this.interceptors.addAll(Arrays.asList(interceptors)); + return this; + } + + @Override + public Builder intercept(double weight, ClientInterceptor... interceptors) { + this.interceptors.addAll(Arrays.asList(interceptors), weight); + return this; + } + + @Override + public Builder marshallerSupplier(MarshallerSupplier supplier) { + this.marshallerSupplier = supplier; + return this; + } + + Builder defaultMarshallerSupplier(MarshallerSupplier supplier) { + this.defaultMarshallerSupplier = Objects.requireNonNullElseGet(supplier, MarshallerSupplier::create); + return this; + } + + @Override + public Builder methodHandler(MethodHandler methodHandler) { + this.methodHandler = methodHandler; + return this; + } + + /** + * Sets the full name of this Method. + * + * @param fullName the full name of the method + * @return this builder instance for fluent API + */ + Builder fullName(String fullName) { + descriptor.setFullMethodName(fullName); + this.name = fullName.substring(fullName.lastIndexOf('/') + 1); + return this; + } + + @Override + public Rules callCredentials(CallCredentials callCredentials) { + this.callCredentials = callCredentials; + return this; + } + + /** + * Builds and returns a new instance of {@link GrpcClientMethodDescriptor}. + * + * @return a new instance of {@link GrpcClientMethodDescriptor} + */ + @Override + @SuppressWarnings("unchecked") + public GrpcClientMethodDescriptor build() { + MarshallerSupplier supplier = this.marshallerSupplier; + + if (supplier == null) { + supplier = defaultMarshallerSupplier; + } + + if (requestType != null) { + descriptor.setRequestMarshaller((MethodDescriptor.Marshaller) supplier.get(requestType)); + } + + if (responseType != null) { + descriptor.setResponseMarshaller((MethodDescriptor.Marshaller) supplier.get(responseType)); + } + + return new GrpcClientMethodDescriptor(name, + descriptor.build(), + interceptors, + callCredentials, + methodHandler); + } + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientProtocolConfigBlueprint.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientProtocolConfigBlueprint.java new file mode 100644 index 00000000000..bb83ca08017 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientProtocolConfigBlueprint.java @@ -0,0 +1,95 @@ +/* + * 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.webclient.grpc; + +import java.time.Duration; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; +import io.helidon.webclient.spi.ProtocolConfig; + +/** + * Configuration of a gRPC client. + */ +@Prototype.Blueprint +@Prototype.Configured +interface GrpcClientProtocolConfigBlueprint extends ProtocolConfig { + + /** + * Type identifying this protocol. + * + * @return protocol type + */ + @Override + default String type() { + return GrpcProtocolProvider.CONFIG_KEY; + } + + /** + * Name identifying this client protocol. Defaults to type. + * + * @return name of client protocol + */ + @Option.Configured + @Option.Default(GrpcProtocolProvider.CONFIG_KEY) + @Override + String name(); + + /** + * How long to wait for the next HTTP/2 data frame to arrive in underlying stream. + * Whether this is a fatal error or not is controlled by {@link #abortPollTimeExpired()}. + * + * @return poll time as a duration + * @see io.helidon.common.socket.SocketOptions#readTimeout() + */ + @Option.Configured + @Option.Default("PT10S") + Duration pollWaitTime(); + + /** + * Whether to continue retrying after a poll wait timeout expired or not. If a read + * operation timeouts out and this flag is set to {@code false}, the event is logged + * and the client will retry. Otherwise, an exception is thrown. + * + * @return abort timeout flag + */ + @Option.Configured + @Option.Default("false") + boolean abortPollTimeExpired(); + + /** + * How often to send a heartbeat (HTTP/2 ping) to check if the connection is still + * alive. This is useful for long-running, streaming gRPC calls. It is turned off by + * default but can be enabled by setting the period to a value greater than 0. + * + * @return heartbeat period + */ + @Option.Configured + @Option.Default("PT0S") + Duration heartbeatPeriod(); + + /** + * Initial buffer size used to serialize gRPC request payloads. Buffers shall grow + * according to the payload size, but setting this initial buffer size to a larger value + * may improve performance for certain applications. + * + * @return initial buffer size + */ + @Option.Configured + @Option.Default("2048") + int initBufferSize(); +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientStream.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientStream.java new file mode 100644 index 00000000000..3784f9ed92a --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcClientStream.java @@ -0,0 +1,37 @@ +/* + * 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.webclient.grpc; + +import io.helidon.common.socket.SocketContext; +import io.helidon.http.http2.Http2Settings; +import io.helidon.webclient.http2.Http2ClientConfig; +import io.helidon.webclient.http2.Http2ClientConnection; +import io.helidon.webclient.http2.Http2ClientStream; +import io.helidon.webclient.http2.Http2StreamConfig; +import io.helidon.webclient.http2.LockingStreamIdSequence; + +class GrpcClientStream extends Http2ClientStream { + + GrpcClientStream(Http2ClientConnection connection, + Http2Settings serverSettings, + SocketContext ctx, + Http2StreamConfig http2StreamConfig, + Http2ClientConfig http2ClientConfig, + LockingStreamIdSequence streamIdSeq) { + super(connection, serverSettings, ctx, http2StreamConfig, http2ClientConfig, streamIdSeq); + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcProtocolProvider.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcProtocolProvider.java new file mode 100644 index 00000000000..337bfeaa710 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcProtocolProvider.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. + */ + +package io.helidon.webclient.grpc; + +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.spi.ClientProtocolProvider; + +/** + * Provider for {@link GrpcClient}. + */ +public class GrpcProtocolProvider implements ClientProtocolProvider { + static final String CONFIG_KEY = "grpc"; + + /** + * Public constructor required by {@link java.util.ServiceLoader}. + */ + public GrpcProtocolProvider() { + } + + @Override + public String protocolId() { + return GrpcClient.PROTOCOL_ID; + } + + @Override + public Class configType() { + return GrpcClientProtocolConfig.class; + } + + @Override + public GrpcClientProtocolConfig defaultConfig() { + return GrpcClientProtocolConfig.create(); + } + + @Override + public GrpcClient protocol(WebClient client, GrpcClientProtocolConfig config) { + return new GrpcClientImpl(client, + GrpcClientConfig.builder() + .from(client.prototype()) + .protocolConfig(config) + .buildPrototype()); + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClient.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClient.java new file mode 100644 index 00000000000..831e2a21095 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClient.java @@ -0,0 +1,124 @@ +/* + * 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.webclient.grpc; + +import java.util.Iterator; + +import io.grpc.stub.StreamObserver; + +/** + * Client for a single service. + * + * @see io.helidon.webclient.grpc.GrpcClient#serviceClient(io.helidon.webclient.grpc.GrpcServiceDescriptor) + */ +public interface GrpcServiceClient { + + /** + * Name of the service this client was created for. + * + * @return service name + */ + String serviceName(); + + /** + * Blocking gRPC unary call. + * + * @param methodName method name + * @param request the request + * @param type of request + * @param type of response + * @return the response + */ + ResT unary(String methodName, ReqT request); + + /** + * Asynchronous gRPC unary call. + * + * @param methodName method name + * @param request the request + * @param response the response observer + * @param type of request + * @param type of response + */ + void unary(String methodName, ReqT request, StreamObserver response); + + /** + * Blocking gRPC server stream call. + * + * @param methodName method name + * @param request the request + * @param type of request + * @param type of response + * @return the response iterator + */ + Iterator serverStream(String methodName, ReqT request); + + /** + * Asynchronous gRPC server stream call. + * + * @param methodName method name + * @param request the request + * @param response the response observer + * @param type of request + * @param type of response + */ + void serverStream(String methodName, ReqT request, StreamObserver response); + + /** + * Blocking gRPC client stream call. + * + * @param methodName method name + * @param request the request iterator + * @param type of request + * @param type of response + * @return the response + */ + ResT clientStream(String methodName, Iterator request); + + /** + * Asynchronous gRPC client stream call. + * + * @param methodName method name + * @param response the response observer + * @param type of request + * @param type of response + * @return the request observer + */ + StreamObserver clientStream(String methodName, StreamObserver response); + + /** + * gRPC bidirectional call using {@link Iterator}. + * + * @param methodName method name + * @param request request iterator + * @param type of request + * @param type of response + * @return response iterator + */ + Iterator bidi(String methodName, Iterator request); + + /** + * gRPC bidirectional call using {@link StreamObserver}. + * + * @param methodName method name + * @param response the response observer + * @param type of request + * @param type of response + * @return the request observer + */ + StreamObserver bidi(String methodName, StreamObserver response); +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClientImpl.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClientImpl.java new file mode 100644 index 00000000000..e9faafaa899 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceClientImpl.java @@ -0,0 +1,200 @@ +/* + * 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.webclient.grpc; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import io.helidon.grpc.core.WeightedBag; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ClientInterceptors; +import io.grpc.MethodDescriptor; +import io.grpc.stub.ClientCalls; +import io.grpc.stub.StreamObserver; + +class GrpcServiceClientImpl implements GrpcServiceClient { + private final GrpcServiceDescriptor serviceDescriptor; + private final Channel serviceChannel; + private final GrpcClientImpl grpcClient; + private final Map methodCache = new ConcurrentHashMap<>(); + + GrpcServiceClientImpl(GrpcServiceDescriptor descriptor, GrpcClientImpl grpcClient) { + this.serviceDescriptor = descriptor; + this.grpcClient = grpcClient; + + if (descriptor.interceptors().isEmpty()) { + serviceChannel = grpcClient.channel(); + } else { + // sort interceptors using a weighted bag + WeightedBag interceptors = WeightedBag.create(); + for (ClientInterceptor interceptor : descriptor.interceptors()) { + interceptors.add(interceptor); + } + + // wrap channel to call interceptors -- reversed for composition + List orderedInterceptors = interceptors.stream().toList().reversed(); + serviceChannel = ClientInterceptors.intercept(grpcClient.channel(), orderedInterceptors); + } + } + + @Override + public String serviceName() { + return serviceDescriptor.serviceName(); + } + + @Override + public ResT unary(String methodName, ReqT request) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.UNARY); + return ClientCalls.blockingUnaryCall(call, request); + } + + @Override + public void unary(String methodName, ReqT request, StreamObserver response) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.UNARY); + ClientCalls.asyncUnaryCall(call, request, response); + } + + @Override + public Iterator serverStream(String methodName, ReqT request) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.SERVER_STREAMING); + return ClientCalls.blockingServerStreamingCall(call, request); + } + + @Override + public void serverStream(String methodName, ReqT request, StreamObserver response) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.SERVER_STREAMING); + ClientCalls.asyncServerStreamingCall(call, request, response); + } + + @Override + public ResT clientStream(String methodName, Iterator request) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.CLIENT_STREAMING); + CompletableFuture future = new CompletableFuture<>(); + StreamObserver observer = ClientCalls.asyncClientStreamingCall(call, new StreamObserver<>() { + private ResT value; + + @Override + public void onNext(ResT value) { + this.value = value; + } + + @Override + public void onError(Throwable t) { + future.completeExceptionally(t); + } + + @Override + public void onCompleted() { + future.complete(value); + } + }); + + // send client stream + while (request.hasNext()) { + observer.onNext(request.next()); + } + observer.onCompleted(); + + // block waiting for response + try { + return future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public StreamObserver clientStream(String methodName, StreamObserver response) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.CLIENT_STREAMING); + return ClientCalls.asyncClientStreamingCall(call, response); + } + + @Override + public Iterator bidi(String methodName, Iterator request) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.BIDI_STREAMING); + CompletableFuture> future = new CompletableFuture<>(); + StreamObserver observer = ClientCalls.asyncBidiStreamingCall(call, new StreamObserver<>() { + private final List values = new ArrayList<>(); + + @Override + public void onNext(ResT value) { + values.add(value); + } + + @Override + public void onError(Throwable t) { + future.completeExceptionally(t); + } + + @Override + public void onCompleted() { + future.complete(values.iterator()); + } + }); + + // send client stream + while (request.hasNext()) { + observer.onNext(request.next()); + } + observer.onCompleted(); + + // block waiting for response + try { + return future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public StreamObserver bidi(String methodName, StreamObserver response) { + ClientCall call = ensureMethod(methodName, MethodDescriptor.MethodType.BIDI_STREAMING); + return ClientCalls.asyncBidiStreamingCall(call, response); + } + + private ClientCall ensureMethod(String methodName, MethodDescriptor.MethodType methodType) { + GrpcClientMethodDescriptor methodDescriptor = serviceDescriptor.method(methodName); + if (!methodDescriptor.type().equals(methodType)) { + throw new IllegalArgumentException("Method " + methodName + " is of type " + methodDescriptor.type() + + ", yet " + methodType + " was requested."); + } + + // use channel that contains all service and method interceptors + if (methodDescriptor.interceptors().isEmpty()) { + return serviceChannel.newCall(methodDescriptor.descriptor(), CallOptions.DEFAULT); + } else { + Channel methodChannel = methodCache.computeIfAbsent(methodName, k -> { + WeightedBag interceptors = WeightedBag.create(); + for (ClientInterceptor interceptor : serviceDescriptor.interceptors()) { + interceptors.add(interceptor); + } + interceptors.merge(methodDescriptor.interceptors()); + List orderedInterceptors = interceptors.stream().toList().reversed(); + return ClientInterceptors.intercept(grpcClient.channel(), orderedInterceptors); + }); + return methodChannel.newCall(methodDescriptor.descriptor(), CallOptions.DEFAULT); + } + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceDescriptorBlueprint.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceDescriptorBlueprint.java new file mode 100644 index 00000000000..6018915b50e --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcServiceDescriptorBlueprint.java @@ -0,0 +1,76 @@ +/* + * 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.webclient.grpc; + +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; + +import io.grpc.CallCredentials; +import io.grpc.ClientInterceptor; + +@Prototype.Blueprint +interface GrpcServiceDescriptorBlueprint { + + /** + * Service name. + * + * @return the server name + */ + String serviceName(); + + /** + * Map of names to gRPC method descriptors. + * + * @return method map + */ + @Option.Singular + Map methods(); + + /** + * Descriptor for a given method. + * + * @param name method name + * @return method descriptor + * @throws NoSuchElementException if not found + */ + default GrpcClientMethodDescriptor method(String name) { + GrpcClientMethodDescriptor descriptor = methods().get(name); + if (descriptor == null) { + throw new NoSuchElementException("There is no method " + name + " defined for service " + this); + } + return descriptor; + } + + /** + * Ordered list of method interceptors. + * + * @return list of interceptors + */ + @Option.Singular + List interceptors(); + + /** + * Credentials for this call, if any. + * + * @return optional credentials + */ + Optional callCredentials(); +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcUnaryClientCall.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcUnaryClientCall.java new file mode 100644 index 00000000000..2ce34db5d5b --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcUnaryClientCall.java @@ -0,0 +1,135 @@ +/* + * 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.webclient.grpc; + +import io.helidon.common.buffers.BufferData; +import io.helidon.http.http2.Http2FrameData; +import io.helidon.webclient.http2.StreamTimeoutException; + +import io.grpc.CallOptions; +import io.grpc.MethodDescriptor; +import io.grpc.Status; + +import static java.lang.System.Logger.Level.DEBUG; +import static java.lang.System.Logger.Level.ERROR; + +/** + * An implementation of a unary gRPC call. Expects: + *

    + * start request sendMessage (halfClose | cancel) + * + * @param request type + * @param response type + */ +class GrpcUnaryClientCall extends GrpcBaseClientCall { + private static final System.Logger LOGGER = System.getLogger(GrpcUnaryClientCall.class.getName()); + + private volatile boolean closeCalled; + private volatile boolean requestSent; + private volatile boolean responseSent; + + GrpcUnaryClientCall(GrpcClientImpl grpcClient, + MethodDescriptor methodDescriptor, + CallOptions callOptions) { + super(grpcClient, methodDescriptor, callOptions); + } + + @Override + public void request(int numMessages) { + socket().log(LOGGER, DEBUG, "request called %d", numMessages); + if (numMessages < 1) { + close(Status.INVALID_ARGUMENT); + } + } + + @Override + public void cancel(String message, Throwable cause) { + socket().log(LOGGER, DEBUG, "cancel called %s", message); + close(Status.CANCELLED); + } + + @Override + public void halfClose() { + socket().log(LOGGER, DEBUG, "halfClose called"); + close(responseSent ? Status.OK : Status.UNKNOWN); + } + + @Override + public void sendMessage(ReqT message) { + // should only be called once + if (requestSent) { + close(Status.FAILED_PRECONDITION); + return; + } + + // serialize and write message + byte[] serialized = serializeMessage(message); + BufferData messageData = BufferData.createReadOnly(serialized, 0, serialized.length); + BufferData headerData = BufferData.create(5); + headerData.writeInt8(0); // no compression + headerData.writeUnsignedInt32(messageData.available()); // length prefixed + clientStream().writeData(BufferData.create(headerData, messageData), true); + requestSent = true; + + // read response headers + clientStream().readHeaders(); + + while (isRemoteOpen()) { + // trailers or eos received? + if (clientStream().trailers().isDone() || !clientStream().hasEntity()) { + socket().log(LOGGER, DEBUG, "[Reading thread] trailers or eos received"); + break; + } + + // attempt to read and queue + Http2FrameData frameData; + try { + frameData = clientStream().readOne(pollWaitTime()); + } catch (StreamTimeoutException e) { + // abort or retry based on config settings + if (abortPollTimeExpired()) { + socket().log(LOGGER, ERROR, "[Reading thread] HTTP/2 stream timeout, aborting"); + responseListener().onClose(Status.DEADLINE_EXCEEDED, EMPTY_METADATA); + break; + } + socket().log(LOGGER, ERROR, "[Reading thread] HTTP/2 stream timeout, retrying"); + continue; + } + if (frameData != null) { + socket().log(LOGGER, DEBUG, "response received"); + responseListener().onMessage(toResponse(frameData.data())); + responseSent = true; + } + } + } + + @Override + protected void startStreamingThreads() { + // no-op + } + + private void close(Status status) { + if (!closeCalled) { + socket().log(LOGGER, DEBUG, "closing client call"); + responseListener().onClose(status, EMPTY_METADATA); + clientStream().cancel(); + connection().close(); + unblockUnaryExecutor(); + closeCalled = true; + } + } +} diff --git a/webclient/grpc/src/main/java/io/helidon/webclient/grpc/package-info.java b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/package-info.java new file mode 100644 index 00000000000..f7918d9e325 --- /dev/null +++ b/webclient/grpc/src/main/java/io/helidon/webclient/grpc/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 WebClient HTTP/1.1 Support. + */ +package io.helidon.webclient.grpc; diff --git a/webclient/grpc/src/main/java/module-info.java b/webclient/grpc/src/main/java/module-info.java new file mode 100644 index 00000000000..fc0984efc0c --- /dev/null +++ b/webclient/grpc/src/main/java/module-info.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. + */ + +import io.helidon.common.features.api.Feature; +import io.helidon.common.features.api.HelidonFlavor; + +/** + * Helidon WebClient gRPC Support. + */ +@Feature(value = "gRPC", + description = "WebClient gRPC support", + in = HelidonFlavor.SE, + path = {"WebClient", "gRPC"} +) +module io.helidon.webclient.grpc { + + requires static io.helidon.common.features.api; + + requires transitive io.grpc; + requires transitive io.grpc.stub; + requires transitive io.helidon.builder.api; + requires transitive io.helidon.webclient.http2; + requires transitive io.helidon.webclient; + + requires io.helidon.grpc.core; + + exports io.helidon.webclient.grpc; + + provides io.helidon.webclient.spi.ClientProtocolProvider + with io.helidon.webclient.grpc.GrpcProtocolProvider; +} \ No newline at end of file diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2Client.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2Client.java index c1d35f6c7a3..72fc534aab1 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2Client.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2Client.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. @@ -22,6 +22,7 @@ import io.helidon.common.config.Config; import io.helidon.webclient.api.HttpClient; import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.spi.Protocol; /** * HTTP2 client. @@ -32,6 +33,11 @@ public interface Http2Client extends HttpClient, RuntimeType * HTTP/2 protocol ID, as used by ALPN. */ String PROTOCOL_ID = "h2"; + /** + * Protocol to use to obtain an instance of http/2 specific client from + * {@link io.helidon.webclient.api.WebClient#client(io.helidon.webclient.spi.Protocol)}. + */ + Protocol PROTOCOL = Http2ProtocolProvider::new; /** * A new fluent API builder to customize client setup. diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnection.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnection.java index 71a2ff21139..fa92a9e7561 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnection.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnection.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. @@ -61,7 +61,10 @@ import static java.lang.System.Logger.Level.TRACE; import static java.lang.System.Logger.Level.WARNING; -class Http2ClientConnection { +/** + * Represents an HTTP2 connection on the client. + */ +public class Http2ClientConnection { private static final System.Logger LOGGER = System.getLogger(Http2ClientConnection.class.getName()); private static final int FRAME_HEADER_LENGTH = 9; private final Http2FrameListener sendListener = new Http2LoggingFrameListener("cl-send"); @@ -103,7 +106,15 @@ class Http2ClientConnection { this.writer = new Http2ConnectionWriter(connection.helidonSocket(), connection.writer(), List.of()); } - static Http2ClientConnection create(Http2ClientImpl http2Client, + /** + * Creates an HTTP2 client connection. + * + * @param http2Client the HTTP2 client + * @param connection the client connection + * @param sendSettings whether to send the settings or not + * @return an HTTP2 client connection + */ + public static Http2ClientConnection create(Http2ClientImpl http2Client, ClientConnection connection, boolean sendSettings) { @@ -136,6 +147,15 @@ Http2ClientStream stream(int streamId) { } + /** + * Stream ID sequence. + * + * @return the ID sequence + */ + public LockingStreamIdSequence streamIdSequence() { + return streamIdSeq; + } + Http2ClientStream createStream(Http2StreamConfig config) { //FIXME: priority Http2ClientStream stream = new Http2ClientStream(this, @@ -147,7 +167,13 @@ Http2ClientStream createStream(Http2StreamConfig config) { return stream; } - void addStream(int streamId, Http2ClientStream stream) { + /** + * Adds a stream to the connection. + * + * @param streamId the stream ID + * @param stream the stream + */ + public void addStream(int streamId, Http2ClientStream stream) { Lock lock = streamsLock.writeLock(); lock.lock(); try { @@ -157,7 +183,12 @@ void addStream(int streamId, Http2ClientStream stream) { } } - void removeStream(int streamId) { + /** + * Removes a stream from the connection. + * + * @param streamId the stream ID + */ + public void removeStream(int streamId) { Lock lock = streamsLock.writeLock(); lock.lock(); try { @@ -202,7 +233,10 @@ void updateLastStreamId(int lastStreamId) { this.lastStreamId = lastStreamId; } - void close() { + /** + * Closes this connection. + */ + public void close() { this.goAway(0, Http2ErrorCode.NO_ERROR, "Closing connection"); if (state.getAndSet(State.CLOSED) != State.CLOSED) { try { diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientImpl.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientImpl.java index 15715d53953..2381459b45e 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientImpl.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientImpl.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. @@ -27,7 +27,10 @@ import io.helidon.webclient.api.WebClient; import io.helidon.webclient.spi.HttpClientSpi; -class Http2ClientImpl implements Http2Client, HttpClientSpi { +/** + * Implementation of HTTP2 client. + */ +public class Http2ClientImpl implements Http2Client, HttpClientSpi { private final WebClient webClient; private final Http2ClientConfig clientConfig; private final Http2ClientProtocolConfig protocolConfig; diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientStream.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientStream.java index 46c82fa33fe..5e9f6735410 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientStream.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientStream.java @@ -40,6 +40,7 @@ import io.helidon.http.http2.Http2Headers; import io.helidon.http.http2.Http2HuffmanDecoder; import io.helidon.http.http2.Http2LoggingFrameListener; +import io.helidon.http.http2.Http2Ping; import io.helidon.http.http2.Http2Priority; import io.helidon.http.http2.Http2RstStream; import io.helidon.http.http2.Http2Setting; @@ -53,10 +54,16 @@ import static java.lang.System.Logger.Level.DEBUG; -class Http2ClientStream implements Http2Stream, ReleasableResource { +/** + * Represents an HTTP2 client stream. This class is not intended to be used by + * applications, it is only public internally within Helidon. + */ +public class Http2ClientStream implements Http2Stream, ReleasableResource { private static final System.Logger LOGGER = System.getLogger(Http2ClientStream.class.getName()); private static final Set NON_CANCELABLE = Set.of(Http2StreamState.CLOSED, Http2StreamState.IDLE); + private static final Http2FrameData HTTP2_PING = Http2Ping.create().toFrameData(); + private final Http2ClientConnection connection; private final Http2Settings serverSettings; private final SocketContext ctx; @@ -80,7 +87,7 @@ class Http2ClientStream implements Http2Stream, ReleasableResource { private int streamId; private StreamBuffer buffer; - Http2ClientStream(Http2ClientConnection connection, + protected Http2ClientStream(Http2ClientConnection connection, Http2Settings serverSettings, SocketContext ctx, Http2StreamConfig http2StreamConfig, @@ -111,6 +118,7 @@ public void headers(Http2Headers headers, boolean endOfStream) { this.currentHeaders = headers; this.hasEntity = !endOfStream; } + @Override public boolean rstStream(Http2RstStream rstStream) { if (state == Http2StreamState.IDLE) { @@ -181,15 +189,29 @@ void trailers(Http2Headers headers, boolean endOfStream) { trailers.complete(headers.httpHeaders()); } - CompletableFuture trailers() { + /** + * Future that shall be completed once trailers are received. + * + * @return the completable future + */ + public CompletableFuture trailers() { return trailers; } - boolean hasEntity() { + /** + * Determines if an entity is expected. Set to {@code false} when an EOS flag + * is received. + * + * @return {@code true} if entity expected, {@code false} otherwise. + */ + public boolean hasEntity() { return hasEntity; } - void cancel() { + /** + * Cancels this client stream. + */ + public void cancel() { if (NON_CANCELABLE.contains(state)) { return; } @@ -205,7 +227,10 @@ void cancel() { } } - void close() { + /** + * Removes the stream from underlying connection. + */ + public void close() { connection.removeStream(streamId); } @@ -226,7 +251,12 @@ BufferData read(int i) { return read(); } - BufferData read() { + /** + * Reads a buffer data from the stream. + * + * @return the buffer data + */ + public BufferData read() { while (state == Http2StreamState.HALF_CLOSED_LOCAL && readState != ReadState.END && hasEntity) { Http2FrameData frameData = readOne(timeout); if (frameData != null) { @@ -257,7 +287,13 @@ Status waitFor100Continue() { return null; } - void writeHeaders(Http2Headers http2Headers, boolean endOfStream) { + /** + * Writes HTTP2 headers to the stream. + * + * @param http2Headers the headers + * @param endOfStream end of stream marker + */ + public void writeHeaders(Http2Headers http2Headers, boolean endOfStream) { this.state = Http2StreamState.checkAndGetState(this.state, Http2FrameType.HEADERS, true, endOfStream, true); this.readState = readState.check(http2Headers.httpHeaders().contains(HeaderValues.EXPECT_100) ? ReadState.CONTINUE_100_HEADERS @@ -293,7 +329,13 @@ void writeHeaders(Http2Headers http2Headers, boolean endOfStream) { } } - void writeData(BufferData entityBytes, boolean endOfStream) { + /** + * Writes a buffer data into the stream. + * + * @param entityBytes buffer data + * @param endOfStream end of stream marker + */ + public void writeData(BufferData entityBytes, boolean endOfStream) { Http2FrameHeader frameHeader = Http2FrameHeader.create(entityBytes.available(), Http2FrameTypes.DATA, Http2Flag.DataFlags.create(endOfStream @@ -304,7 +346,19 @@ void writeData(BufferData entityBytes, boolean endOfStream) { splitAndWrite(frameData); } - Http2Headers readHeaders() { + /** + * Sends PING frame to server. Can be used to check if connection is healthy. + */ + public void sendPing() { + connection.writer().write(HTTP2_PING); + } + + /** + * Reads headers from this stream. + * + * @return the headers + */ + public Http2Headers readHeaders() { while (readState == ReadState.HEADERS) { Http2FrameData frameData = readOne(timeout); if (frameData != null) { @@ -314,11 +368,22 @@ Http2Headers readHeaders() { return currentHeaders; } - SocketContext ctx() { + /** + * Returns the socket context associated with the stream. + * + * @return the socket context + */ + public SocketContext ctx() { return ctx; } - private Http2FrameData readOne(Duration pollTimeout) { + /** + * Reads an HTTP2 frame from the stream. + * + * @param pollTimeout timeout + * @return the data frame + */ + public Http2FrameData readOne(Duration pollTimeout) { Http2FrameData frameData = buffer.poll(pollTimeout); if (frameData != null) { diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ConnectionCache.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ConnectionCache.java index 96decead36f..623e5315bb5 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ConnectionCache.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ConnectionCache.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. @@ -29,7 +29,10 @@ import io.helidon.webclient.http1.Http1ClientResponse; import io.helidon.webclient.spi.ClientConnectionCache; -final class Http2ConnectionCache extends ClientConnectionCache { +/** + * A cache of HTTP2 connections. + */ +public final class Http2ConnectionCache extends ClientConnectionCache { private static final Http2ConnectionCache SHARED = new Http2ConnectionCache(true); private final LruCache http2Supported = LruCache.builder() .capacity(1000) @@ -41,11 +44,21 @@ private Http2ConnectionCache(boolean shared) { super(shared); } - static Http2ConnectionCache shared() { + /** + * Returns a reference to the shared connection cache. + * + * @return shared connection cache + */ + public static Http2ConnectionCache shared() { return SHARED; } - static Http2ConnectionCache create() { + /** + * Creates a fresh connection cache. + * + * @return new connection cache + */ + public static Http2ConnectionCache create() { return new Http2ConnectionCache(false); } diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2StreamConfig.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2StreamConfig.java index 1e9e6cbd1b7..61cffcf6b9d 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2StreamConfig.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2StreamConfig.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,29 @@ import java.time.Duration; -interface Http2StreamConfig { +/** + * Configuration for an HTTP2 stream. + */ +public interface Http2StreamConfig { + + /** + * Prior knowledge setting. + * + * @return prior knowledge setting + */ boolean priorKnowledge(); + /** + * Stream priority. + * + * @return the stream priority + */ int priority(); + /** + * Read timeout for this stream. + * + * @return the timeout + */ Duration readTimeout(); } diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/LockingStreamIdSequence.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/LockingStreamIdSequence.java index 51b605286e5..cb6b53c0084 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/LockingStreamIdSequence.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/LockingStreamIdSequence.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,17 +19,20 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -class LockingStreamIdSequence { +/** + * A stream ID sequence that uses locks for concurrency. + */ +public class LockingStreamIdSequence { private final AtomicInteger streamIdSeq = new AtomicInteger(0); private final Lock lock = new ReentrantLock(); int lockAndNext() { - lock.lock(); - return streamIdSeq.updateAndGet(o -> o % 2 == 0 ? o + 1 : o + 2); + lock.lock(); + return streamIdSeq.updateAndGet(o -> o % 2 == 0 ? o + 1 : o + 2); } - void unlock(){ + void unlock() { lock.unlock(); } } diff --git a/webclient/pom.xml b/webclient/pom.xml index 818b8621299..e2ee8899811 100644 --- a/webclient/pom.xml +++ b/webclient/pom.xml @@ -41,6 +41,7 @@ tracing webclient websocket + grpc diff --git a/webclient/tests/grpc/pom.xml b/webclient/tests/grpc/pom.xml new file mode 100644 index 00000000000..d3a41760e97 --- /dev/null +++ b/webclient/tests/grpc/pom.xml @@ -0,0 +1,148 @@ + + + + 4.0.0 + + io.helidon.webclient.tests + helidon-webclient-tests-project + 4.0.0-SNAPSHOT + + + helidon-webclient-tests-grpc + Helidon WebClient gRPC Tests + + + + + javax.annotation + javax.annotation-api + 1.3.2 + + + io.helidon.webserver + helidon-webserver + + + io.helidon.webserver + helidon-webserver-http2 + + + io.helidon.webserver + helidon-webserver-websocket + + + io.helidon.webserver + helidon-webserver-grpc + + + io.helidon.webclient + helidon-webclient + + + io.helidon.webclient + helidon-webclient-http2 + + + io.helidon.webclient + helidon-webclient-websocket + + + io.helidon.webclient + helidon-webclient-grpc + + + io.helidon.logging + helidon-logging-common + + + io.helidon.logging + helidon-logging-jul + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5 + test + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5-http2 + test + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5-websocket + test + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5-grpc + test + + + + + + + kr.motd.maven + os-maven-plugin + ${version.plugin.os} + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + + compile + compile-custom + + + + + com.google.protobuf:protoc:${version.lib.google-protobuf}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:${version.lib.grpc}:exe:${os.detected.classifier} + + + + + + diff --git a/webclient/tests/grpc/src/main/proto/events.proto b/webclient/tests/grpc/src/main/proto/events.proto new file mode 100644 index 00000000000..4690d200aa6 --- /dev/null +++ b/webclient/tests/grpc/src/main/proto/events.proto @@ -0,0 +1,60 @@ +/* + * 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. + */ + + +syntax = "proto3"; +option java_package = "io.helidon.webclient.grpc.tests"; + +import "google/protobuf/empty.proto"; + +service EventService { + rpc Send (Message) returns (google.protobuf.Empty) {} + rpc Events (stream EventRequest) returns (stream EventResponse) {} +} + +message Message { + string text = 2; +} + +message EventRequest { + int64 id = 1; + enum Action { + SUBSCRIBE = 0; + UNSUBSCRIBE = 1; + } + Action action = 2; +} + +message EventResponse { + oneof response_type { + Subscribed subscribed = 1; + Unsubscribed unsubscribed = 2; + Event event = 3; + } +} + +message Subscribed { + int64 id = 1; +} + +message Unsubscribed { + int64 id = 1; +} + +message Event { + int64 id = 1; + string text = 2; +} diff --git a/webclient/tests/grpc/src/main/proto/strings.proto b/webclient/tests/grpc/src/main/proto/strings.proto new file mode 100644 index 00000000000..f04386897b4 --- /dev/null +++ b/webclient/tests/grpc/src/main/proto/strings.proto @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 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. + */ + + +syntax = "proto3"; +option java_package = "io.helidon.webclient.grpc.tests"; + +service StringService { + rpc Upper (StringMessage) returns (StringMessage) {} + rpc Split (StringMessage) returns (stream StringMessage) {} + rpc Join (stream StringMessage) returns (StringMessage) {} + rpc Echo (stream StringMessage) returns (stream StringMessage) {} + rpc BadMethod (StringMessage) returns (StringMessage) {} + rpc NotImplementedMethod (StringMessage) returns (StringMessage) {} +} + +message StringMessage { + string text = 1; +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcBaseTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcBaseTest.java new file mode 100644 index 00000000000..82897279b5a --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcBaseTest.java @@ -0,0 +1,249 @@ +/* + * 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.webclient.grpc.tests; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.MethodDescriptor; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; +import io.helidon.common.Weight; +import io.helidon.common.configurable.Resource; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.grpc.GrpcRouting; +import io.helidon.webserver.testing.junit5.SetUpRoute; +import io.helidon.webserver.testing.junit5.SetUpServer; +import org.junit.jupiter.api.BeforeEach; + +class GrpcBaseTest { + + private final List> calledInterceptors = new CopyOnWriteArrayList<>(); + + protected List> calledInterceptors() { + return calledInterceptors; + } + + @SetUpServer + public static void setup(WebServerConfig.Builder builder) { + builder.tls(tls -> tls.privateKey(key -> key + .keystore(store -> store + .passphrase("password") + .keystore(Resource.create("server.p12")))) + .privateKeyCertChain(key -> key + .keystore(store -> store + .trustStore(true) + .passphrase("password") + .keystore(Resource.create("server.p12"))))); + } + + @SetUpRoute + static void setUpRoute(GrpcRouting.Builder routing) { + routing.unary(Strings.getDescriptor(), + "StringService", + "Upper", + GrpcStubTest::upper) + .serverStream(Strings.getDescriptor(), + "StringService", + "Split", + GrpcStubTest::split) + .clientStream(Strings.getDescriptor(), + "StringService", + "Join", + GrpcStubTest::join) + .bidi(Strings.getDescriptor(), + "StringService", + "Echo", + GrpcStubTest::echo) + .unary(Strings.getDescriptor(), + "StringService", + "BadMethod", + GrpcStubTest::badMethod); + } + + @BeforeEach + void setUpTest() { + calledInterceptors.clear(); + } + + static void upper(Strings.StringMessage req, + StreamObserver streamObserver) { + Strings.StringMessage msg = Strings.StringMessage.newBuilder() + .setText(req.getText().toUpperCase(Locale.ROOT)) + .build(); + streamObserver.onNext(msg); + streamObserver.onCompleted(); + } + + static void split(Strings.StringMessage req, + StreamObserver streamObserver) { + String reqString = req.getText(); + if (!reqString.isEmpty()) { + String[] strings = reqString.split(" "); + for (String s : strings) { + streamObserver.onNext(Strings.StringMessage.newBuilder() + .setText(s) + .build()); + + } + } + streamObserver.onCompleted(); + } + + static StreamObserver join(StreamObserver streamObserver) { + return new StreamObserver<>() { + private StringBuilder builder; + + @Override + public void onNext(Strings.StringMessage value) { + if (builder == null) { + builder = new StringBuilder(); + builder.append(value.getText()); + } else { + builder.append(" ").append(value.getText()); + } + } + + @Override + public void onError(Throwable t) { + streamObserver.onError(t); + } + + @Override + public void onCompleted() { + streamObserver.onNext(Strings.StringMessage.newBuilder() + .setText(builder.toString()) + .build()); + streamObserver.onCompleted(); + } + }; + } + + static StreamObserver echo(StreamObserver streamObserver) { + return new StreamObserver<>() { + @Override + public void onNext(Strings.StringMessage value) { + streamObserver.onNext(value); + } + + @Override + public void onError(Throwable t) { + streamObserver.onError(t); + } + + @Override + public void onCompleted() { + streamObserver.onCompleted(); + } + }; + } + + static void badMethod(Strings.StringMessage req, StreamObserver streamObserver) { + streamObserver.onError(Status.INTERNAL.asException()); + } + + Strings.StringMessage newStringMessage(String data) { + return Strings.StringMessage.newBuilder().setText(data).build(); + } + + static StreamObserver singleStreamObserver(CompletableFuture future) { + return new StreamObserver<>() { + private ReqT value; + + @Override + public void onNext(ReqT value) { + this.value = value; + } + + @Override + public void onError(Throwable t) { + future.completeExceptionally(t); + } + + @Override + public void onCompleted() { + future.complete(value); + } + }; + } + + static StreamObserver multiStreamObserver(CompletableFuture> future) { + return new StreamObserver<>() { + private final List value = new ArrayList<>(); + + @Override + public void onNext(ResT value) { + this.value.add(value); + } + + @Override + public void onError(Throwable t) { + future.completeExceptionally(t); + } + + @Override + public void onCompleted() { + future.complete(value.iterator()); + } + }; + } + + class BaseInterceptor implements ClientInterceptor { + @Override + public ClientCall interceptCall(MethodDescriptor method, + CallOptions callOptions, + Channel next) { + calledInterceptors.add(getClass()); + return next.newCall(method, callOptions); + } + } + + @Weight(10.0) + class Weight10Interceptor extends BaseInterceptor { + } + + @Weight(50.0) + class Weight50Interceptor extends BaseInterceptor { + } + + @Weight(100.0) + class Weight100Interceptor extends BaseInterceptor { + } + + @Weight(500.0) + class Weight500Interceptor extends BaseInterceptor { + } + + @Weight(1000.0) + class Weight1000Interceptor extends BaseInterceptor { + } + + List allInterceptors() { + return List.of(new Weight10Interceptor(), + new Weight50Interceptor(), + new Weight100Interceptor(), + new Weight500Interceptor(), + new Weight1000Interceptor()); + } +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConfigProtocolTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConfigProtocolTest.java new file mode 100644 index 00000000000..86125518fc0 --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConfigProtocolTest.java @@ -0,0 +1,52 @@ +/* + * 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.webclient.grpc.tests; + +import java.time.Duration; + +import io.helidon.config.Config; +import io.helidon.config.ConfigSources; +import io.helidon.webclient.grpc.GrpcClientProtocolConfig; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * Tests gRPC client config protocol settings. + */ +class GrpcConfigProtocolTest { + + @Test + void testDefaults() { + GrpcClientProtocolConfig config = GrpcClientProtocolConfig.create(); + assertThat(config.pollWaitTime(), is(Duration.ofSeconds(10))); + assertThat(config.abortPollTimeExpired(), is(false)); + assertThat(config.initBufferSize(), is(2048)); + assertThat(config.heartbeatPeriod(), is(Duration.ofSeconds(0))); + } + + @Test + void testApplicationConfig() { + GrpcClientProtocolConfig config = GrpcClientProtocolConfig.create( + Config.create(ConfigSources.classpath("application.yaml")).get("grpc-client")); + assertThat(config.pollWaitTime(), is(Duration.ofSeconds(30))); + assertThat(config.abortPollTimeExpired(), is(true)); + assertThat(config.initBufferSize(), is(10000)); + assertThat(config.heartbeatPeriod(), is(Duration.ofSeconds(10))); + } +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConnectionErrorTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConnectionErrorTest.java new file mode 100644 index 00000000000..27862b5a56f --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcConnectionErrorTest.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.webclient.grpc.tests; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import io.grpc.stub.StreamObserver; +import io.helidon.common.configurable.Resource; +import io.helidon.common.tls.Tls; +import io.helidon.webclient.grpc.GrpcClient; +import io.helidon.webclient.grpc.GrpcClientMethodDescriptor; +import io.helidon.webclient.grpc.GrpcClientProtocolConfig; +import io.helidon.webclient.grpc.GrpcServiceDescriptor; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.testing.junit5.ServerTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Tests server connection problems, such as a disappearing server. + */ +@ServerTest +class GrpcConnectionErrorTest extends GrpcBaseTest { + private static final long TIMEOUT_SECONDS = 10; + + private final WebServer server; + private final GrpcClient grpcClient; + private final GrpcServiceDescriptor serviceDescriptor; + + private GrpcConnectionErrorTest(WebServer server) { + this.server = server; + Tls clientTls = Tls.builder() + .trust(trust -> trust + .keystore(store -> store + .passphrase("password") + .trustStore(true) + .keystore(Resource.create("client.p12")))) + .build(); + GrpcClientProtocolConfig config = GrpcClientProtocolConfig.builder() + .heartbeatPeriod(Duration.ofSeconds(1)) // detects failure faster + .build(); + this.grpcClient = GrpcClient.builder() + .tls(clientTls) + .protocolConfig(config) + .baseUri("https://localhost:" + server.port()) + .build(); + this.serviceDescriptor = GrpcServiceDescriptor.builder() + .serviceName("StringService") + .putMethod("Join", + GrpcClientMethodDescriptor.clientStreaming("StringService", "Join") + .requestType(Strings.StringMessage.class) + .responseType(Strings.StringMessage.class) + .build()) + .build(); + } + + @Test + void testClientStreamingWithError() throws InterruptedException { + CompletableFuture future = new CompletableFuture<>(); + StreamObserver req = grpcClient.serviceClient(serviceDescriptor) + .clientStream("Join", singleStreamObserver(future)); + req.onNext(newStringMessage("hello")); + server.stop(); // kill server! + assertEventually(future::isCompletedExceptionally, TIMEOUT_SECONDS * 1000); + } + + private static void assertEventually(Supplier predicate, long millis) throws InterruptedException { + long start = System.currentTimeMillis(); + do { + if (predicate.get()) { + return; + } + Thread.sleep(100); + } while (System.currentTimeMillis() - start <= millis); + fail("Predicate failed after " + millis + " milliseconds"); + } +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorStubTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorStubTest.java new file mode 100644 index 00000000000..c602c488349 --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorStubTest.java @@ -0,0 +1,67 @@ +/* + * 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.webclient.grpc.tests; + +import io.grpc.Channel; +import io.helidon.common.configurable.Resource; +import io.helidon.common.tls.Tls; +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.grpc.GrpcClient; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.testing.junit5.ServerTest; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; + +/** + * Tests gRPC client using stubs and TLS. + */ +@ServerTest +class GrpcInterceptorStubTest extends GrpcBaseTest { + + private final WebClient webClient; + + private GrpcInterceptorStubTest(WebServer server) { + Tls clientTls = Tls.builder() + .trust(trust -> trust + .keystore(store -> store + .passphrase("password") + .trustStore(true) + .keystore(Resource.create("client.p12")))) + .build(); + this.webClient = WebClient.builder() + .tls(clientTls) + .baseUri("https://localhost:" + server.port()) + .build(); + } + + @Test + void testUnaryUpper() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + Channel channel = grpcClient.channel(allInterceptors()); // channel with all interceptors + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(channel); + Strings.StringMessage res = service.upper(newStringMessage("hello")); + assertThat(res.getText(), is("HELLO")); + assertThat(calledInterceptors(), contains(Weight1000Interceptor.class, + Weight500Interceptor.class, + Weight100Interceptor.class, + Weight50Interceptor.class, + Weight10Interceptor.class)); + } +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorTest.java new file mode 100644 index 00000000000..6f279e5e8fd --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcInterceptorTest.java @@ -0,0 +1,79 @@ +/* + * 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.webclient.grpc.tests; + +import io.helidon.common.configurable.Resource; +import io.helidon.common.tls.Tls; +import io.helidon.webclient.grpc.GrpcClient; +import io.helidon.webclient.grpc.GrpcClientMethodDescriptor; +import io.helidon.webclient.grpc.GrpcServiceDescriptor; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.testing.junit5.ServerTest; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; + +/** + * Tests client interceptors using low-level API. + */ +@ServerTest +class GrpcInterceptorTest extends GrpcBaseTest { + + private final GrpcClient grpcClient; + private final GrpcServiceDescriptor serviceDescriptor; + + private GrpcInterceptorTest(WebServer server) { + Tls clientTls = Tls.builder() + .trust(trust -> trust + .keystore(store -> store + .passphrase("password") + .trustStore(true) + .keystore(Resource.create("client.p12")))) + .build(); + this.grpcClient = GrpcClient.builder() + .tls(clientTls) + .baseUri("https://localhost:" + server.port()) + .build(); + this.serviceDescriptor = GrpcServiceDescriptor.builder() + .serviceName("StringService") + .putMethod("Upper", + GrpcClientMethodDescriptor.unary("StringService", "Upper") + .requestType(Strings.StringMessage.class) + .responseType(Strings.StringMessage.class) + .intercept(new Weight50Interceptor()) + .intercept(new Weight500Interceptor()) + .build()) + .addInterceptor(new Weight100Interceptor()) + .addInterceptor(new Weight1000Interceptor()) + .addInterceptor(new Weight10Interceptor()) + .build(); + } + + @Test + void testUnaryUpper() { + Strings.StringMessage res = grpcClient.serviceClient(serviceDescriptor) + .unary("Upper", newStringMessage("hello")); + assertThat(res.getText(), is("HELLO")); + assertThat(calledInterceptors(), contains(Weight1000Interceptor.class, + Weight500Interceptor.class, + Weight100Interceptor.class, + Weight50Interceptor.class, + Weight10Interceptor.class)); + } +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcStubTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcStubTest.java new file mode 100644 index 00000000000..e1701dca81e --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcStubTest.java @@ -0,0 +1,190 @@ +/* + * 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.webclient.grpc.tests; + +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import io.helidon.common.configurable.Resource; +import io.helidon.common.tls.Tls; +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.grpc.GrpcClient; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.testing.junit5.ServerTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.grpc.stub.StreamObserver; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests gRPC client using stubs and TLS. + */ +@ServerTest +class GrpcStubTest extends GrpcBaseTest { + private static final long TIMEOUT_SECONDS = 10; + + private final WebClient webClient; + + private GrpcStubTest(WebServer server) { + Tls clientTls = Tls.builder() + .trust(trust -> trust + .keystore(store -> store + .passphrase("password") + .trustStore(true) + .keystore(Resource.create("client.p12")))) + .build(); + this.webClient = WebClient.builder() + .tls(clientTls) + .baseUri("https://localhost:" + server.port()) + .build(); + } + + @Test + void testUnaryUpper() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(grpcClient.channel()); + Strings.StringMessage res = service.upper(newStringMessage("hello")); + assertThat(res.getText(), is("HELLO")); + } + + @Test + void tesUnaryUpperLongString() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(grpcClient.channel()); + String s = CharBuffer.allocate(2000).toString().replace('\0', 'a'); + Strings.StringMessage res = service.upper(newStringMessage(s)); + assertThat(res.getText(), is(s.toUpperCase())); + } + + @Test + void testUnaryUpperAsync() throws ExecutionException, InterruptedException, TimeoutException { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceStub service = StringServiceGrpc.newStub(grpcClient.channel()); + CompletableFuture future = new CompletableFuture<>(); + service.upper(newStringMessage("hello"), singleStreamObserver(future)); + Strings.StringMessage res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.getText(), is("HELLO")); + } + + @Test + void testServerStreamingSplit() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(grpcClient.channel()); + Iterator res = service.split(newStringMessage("hello world")); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testServerStreamingSplitEmpty() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(grpcClient.channel()); + Iterator res = service.split(newStringMessage("")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testServerStreamingSplitAsync() throws ExecutionException, InterruptedException, TimeoutException { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceStub service = StringServiceGrpc.newStub(grpcClient.channel()); + CompletableFuture> future = new CompletableFuture<>(); + service.split(newStringMessage("hello world"), multiStreamObserver(future)); + Iterator res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testClientStreamingJoinAsync() throws ExecutionException, InterruptedException, TimeoutException { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceStub service = StringServiceGrpc.newStub(grpcClient.channel()); + CompletableFuture future = new CompletableFuture<>(); + StreamObserver req = service.join(singleStreamObserver(future)); + req.onNext(newStringMessage("hello")); + req.onNext(newStringMessage("world")); + req.onCompleted(); + Strings.StringMessage res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.getText(), is("hello world")); + } + + @Test + void testBidirectionalEchoAsync() throws ExecutionException, InterruptedException, TimeoutException { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceStub service = StringServiceGrpc.newStub(grpcClient.channel()); + CompletableFuture> future = new CompletableFuture<>(); + StreamObserver req = service.echo(multiStreamObserver(future)); + req.onNext(newStringMessage("hello")); + req.onNext(newStringMessage("world")); + req.onCompleted(); + Iterator res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testBidirectionalEchoAsyncEmpty() throws ExecutionException, InterruptedException, TimeoutException { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceStub service = StringServiceGrpc.newStub(grpcClient.channel()); + CompletableFuture> future = new CompletableFuture<>(); + StreamObserver req = service.echo(multiStreamObserver(future)); + req.onCompleted(); + Iterator res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testBidirectionalEchoAsyncWithLargePayload() throws ExecutionException, InterruptedException, TimeoutException { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceStub service = StringServiceGrpc.newStub(grpcClient.channel()); + CompletableFuture> future = new CompletableFuture<>(); + StreamObserver req = service.echo(multiStreamObserver(future)); + byte[] array = new byte[2000]; + new Random().nextBytes(array); + String largeString = new String(array, StandardCharsets.UTF_8); + req.onNext(newStringMessage(largeString)); + req.onCompleted(); + Iterator res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.next().getText(), is(largeString)); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testReceiveServerException() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(grpcClient.channel()); + Assertions.assertThrows(Throwable.class, () -> service.badMethod(newStringMessage("hello"))); + } + + @Test + void testCallingNotImplementMethodThrowsException() { + GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL); + StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(grpcClient.channel()); + Assertions.assertThrows(Throwable.class, () -> service.notImplementedMethod(newStringMessage("hello"))); + } +} diff --git a/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcTest.java b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcTest.java new file mode 100644 index 00000000000..bb7702225ab --- /dev/null +++ b/webclient/tests/grpc/src/test/java/io/helidon/webclient/grpc/tests/GrpcTest.java @@ -0,0 +1,190 @@ +/* + * 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.webclient.grpc.tests; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +import io.helidon.common.configurable.Resource; +import io.helidon.common.tls.Tls; +import io.helidon.webclient.grpc.GrpcClient; +import io.helidon.webclient.grpc.GrpcClientMethodDescriptor; +import io.helidon.webclient.grpc.GrpcServiceDescriptor; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.testing.junit5.ServerTest; + +import org.junit.jupiter.api.Test; +import com.google.protobuf.StringValue; +import io.grpc.stub.StreamObserver; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests gRPC client using low-level API and TLS, no stubs. + */ +@ServerTest +class GrpcTest extends GrpcBaseTest { + private static final long TIMEOUT_SECONDS = 10; + + private final GrpcClient grpcClient; + + private final GrpcServiceDescriptor serviceDescriptor; + + private GrpcTest(WebServer server) { + Tls clientTls = Tls.builder() + .trust(trust -> trust + .keystore(store -> store + .passphrase("password") + .trustStore(true) + .keystore(Resource.create("client.p12")))) + .build(); + this.grpcClient = GrpcClient.builder() + .tls(clientTls) + .baseUri("https://localhost:" + server.port()) + .build(); + + this.serviceDescriptor = GrpcServiceDescriptor.builder() + .serviceName("StringService") + .putMethod("Upper", + GrpcClientMethodDescriptor.unary("StringService", "Upper") + .requestType(Strings.StringMessage.class) + .responseType(Strings.StringMessage.class) + .build()) + .putMethod("Split", + GrpcClientMethodDescriptor.serverStreaming("StringService", "Split") + .requestType(Strings.StringMessage.class) + .responseType(Strings.StringMessage.class) + .build()) + .putMethod("Join", + GrpcClientMethodDescriptor.clientStreaming("StringService", "Join") + .requestType(Strings.StringMessage.class) + .responseType(Strings.StringMessage.class) + .build()) + .putMethod("Echo", + GrpcClientMethodDescriptor.bidirectional("StringService", "Echo") + .requestType(Strings.StringMessage.class) + .responseType(Strings.StringMessage.class) + .build()) + .build(); + } + + @Test + void testUnaryUpper() { + Strings.StringMessage res = grpcClient.serviceClient(serviceDescriptor) + .unary("Upper", newStringMessage("hello")); + assertThat(res.getText(), is("HELLO")); + } + + @Test + void testUnaryUpperAsync() throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture future = new CompletableFuture<>(); + grpcClient.serviceClient(serviceDescriptor) + .unary("Upper", + StringValue.of("hello"), + singleStreamObserver(future)); + Strings.StringMessage res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.getText(), is("HELLO")); + } + + @Test + void testServerStreamingSplit() { + Iterator res = grpcClient.serviceClient(serviceDescriptor) + .serverStream("Split", + newStringMessage("hello world")); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testServerStreamingSplitEmpty() { + Iterator res = grpcClient.serviceClient(serviceDescriptor) + .serverStream("Split", newStringMessage("")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testServerStreamingSplitAsync() throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture> future = new CompletableFuture<>(); + grpcClient.serviceClient(serviceDescriptor) + .serverStream("Split", + newStringMessage("hello world"), + multiStreamObserver(future)); + Iterator res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testClientStreamingJoin() { + Strings.StringMessage res = grpcClient.serviceClient(serviceDescriptor) + .clientStream("Join", List.of(newStringMessage("hello"), + newStringMessage("world")).iterator()); + assertThat(res.getText(), is("hello world")); + } + + @Test + void testClientStreamingJoinAsync() throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture future = new CompletableFuture<>(); + StreamObserver req = grpcClient.serviceClient(serviceDescriptor) + .clientStream("Join", singleStreamObserver(future)); + req.onNext(newStringMessage("hello")); + req.onNext(newStringMessage("world")); + req.onCompleted(); + Strings.StringMessage res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.getText(), is("hello world")); + } + + @Test + void testBidirectionalEcho() { + Iterator res = grpcClient.serviceClient(serviceDescriptor) + .bidi("Echo", List.of(newStringMessage("hello"), + newStringMessage("world")).iterator()); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testBidirectionalEchoEmpty() { + Iterator res = grpcClient.serviceClient(serviceDescriptor) + .bidi("Echo", Collections.emptyIterator()); + assertThat(res.hasNext(), is(false)); + } + + @Test + void testBidirectionalEchoAsync() throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture> future = new CompletableFuture<>(); + StreamObserver req = grpcClient.serviceClient(serviceDescriptor) + .bidi("Echo", multiStreamObserver(future)); + req.onNext(newStringMessage("hello")); + req.onNext(newStringMessage("world")); + req.onCompleted(); + Iterator res = future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertThat(res.next().getText(), is("hello")); + assertThat(res.next().getText(), is("world")); + assertThat(res.hasNext(), is(false)); + } +} diff --git a/webclient/tests/grpc/src/test/resources/application.yaml b/webclient/tests/grpc/src/test/resources/application.yaml new file mode 100644 index 00000000000..84409629c2a --- /dev/null +++ b/webclient/tests/grpc/src/test/resources/application.yaml @@ -0,0 +1,21 @@ +# +# 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. +# + +grpc-client: + poll-wait-time: PT30S + abort-poll-time-expired: true + init-buffer-size: 10000 + heartbeat-period: PT10S \ No newline at end of file diff --git a/webclient/tests/grpc/src/test/resources/client.p12 b/webclient/tests/grpc/src/test/resources/client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..4eb3b8325cd0190163032aca86e1d4fc94856822 GIT binary patch literal 4181 zcmY+EWmFW5wuWIGKw^;Y8d`FO4r!R7kra{cRN$kVp>ybzA%_qK=@29&1VMz6MnqaE zk#3Inu6xf}=f_@qt>@kQ{rNx;WcmQy2M`1q5k4Vbta|J@2$ul21o7w^hgIE5dsUMBr)v#p-`Y6`%P3zDS600VN3FH3R`Xhdjn7`hWbloDoRH)IP6+WcmE9 z`35_>AJFE&UKlM8cz`qTw+lF;|4Y44{>C>)$b58Pwv%K0R(%IXj}~QHjD4NBVYZjc zF@t^Op?!4hS2W1WsUs*fH4rKzE`)r~ui`YVNmNGREmp`fmhZ-l6^5sfB`zPY40}#g zH)^o?@o9hBqDnY3fTdikVzhQYjJ@QFySfUF2zyQ%MJkSx&6yp$d%0u5zcM8tP1i^6 z%}M{O^-J(q-l}? zG;sYG<`hB67OAJ{Kn7v!iM%17ZGOW*_+6?mj_PPjWm!Ul8pMIbf!M^ zWgBVP1D^1uoR+3$()AEJwCJQVGFXYpP?{E!vJd6qcaPa-_wk2fi>CtS#t_Q=YzDhg z_T{E?`*O_~!8+lV;#JIN%WYb`Smp6z>%jU|E0Q|UwSakO!YAE{b)<{PF*8og zVze-4xbFhS3hi4{gv+TZ5{{zgoi6OkC#C!n|C=?7gif&4$VS7{f>r$*glAp@OguSO zga@4c?5V%spuxJq+7~S4uO0~Qtx@pLpVA#omKA$K=0qs_or+yj(CH>JtF!DYfsUdj#Y;j*a+4940>!Es- zM}7u;^}?V7TD&gvN+Kp6EAKaV;pn^2wV@?@vl^3I+gH5IuR|iG=L7^PT$x(L9Pe@2 z?=%-f?!NUux8M^q@js$G+ipgHOQVf`dy{^6RCxNjjhLfHs9_L9h593q(M55&T#~hF z@ZHR#^3^uOTItFm*F)8$)0x`}Zp|%D6NEgD^>WL7HNjNpku0LT6DS&tU+qTrHbUu9 z&)^rxWz@YiTBES@?NpTL!5omgXlbo6pb6g`>>=vt1vu!E<3vBcdqHR_Ba4|Eb_xGg z)pJ##He7jY95Ya1>hZEv-!3&R>CaoXf5J1TZ)6O9K~lf--8>#_9ORnq@zOo*O|s_- zH>^L{5fkgqYi9TBfU#YG%Akqk_WHE*j6&Si&2D2c`4YK3)g}>K$KvCQ7`x9(WDsEy zuQH5qIeN|NDn3hDL$9r!IAXNK`>ba0F{990Ft$^8<0CG8rrA~9aBG;US|w!VjlFOnx;asOlp6HSq&uB> zPp2hwGj;nLOA8~DC4JrPmu=91AuurQp^IZMg8;( zNk?BgR!9GmWnhg;q@`&luP<%zDo;OcX`dMoFJwV0VQyK&8YX2%_YF9}(b}Ac#W$qCfyH z&fn$zp9J^6yo&!nUaeG&iy}_w@w&JB$t=SwN~qqp*!;g?+nl!qbt-3Srz zdUU=~v7Sgb0VsS}2&|z~|3MrAAtr}=3)n1hxrwEjX74xY&WumZJgpiDImLH>0HGEj zE@Pz6+gd485YSPSd-~MF_ofwqC9vZ}8J_A_xnnt@2jF{VD_){puWF0@1B$Q~N$0Vs z-?9BDZRR}ZLhMq|y5-J;bH5!%c&I1&i~bmo_W>~SP^HW#(~yyFh0IX*un{<(Z*H5* zG3|bt;nH9dulFr4zGP97g%w$luP$lQCw&7zUrnj|ct{dHQaAqrM}sXWo* zIwbhHK4sYTi}c}+H=ECU_CW)XPNsksw*O(5%WXUzxJ;CW4wY-#fhx7#RxUv{vGW)O zD08=SPkJ@3D3mcw@tOyy;4L~ar`xu7adVYdzfGQS4)LplDy z?4mahFGu`fqOEB?-}?;U;~_d$fZX81i-)m3$WLiGrYR{Vb(+BxFB{bgmoIBI=R^T$ z2jb^~oKDwEmh%hg@%Nu7LXSBz15KET`V3br4R4cQx~+zqc5kP!I37*ccaqajWobMK zZi-eDvt^V0Wx5yXv-L$NFi$%&6yL^6h{)OAOJ(=Ff06E!^>9J;{ZS0mp#M!8;!a)0 ziI!n_qQZSi)bXC}Av&X{1=>a_R`JQ(6a=y?=Xs3hk0yi^%Qtn{d`)D4<(!F3-FsJ5 zfY|6y$E7qMM(%l|mdCYd7K?3kA#e(=E7g{U4qi2#Z$_ndvFhE0Oh3#!n`A1vNw<3W z2uInJ(>ciz+w6ik()9M}hRO6sepzQ5EC$ppZo^+t8|M>ZX4rADe+_-TVMhM%sw z9eW4be3ydc95|A1=ww9x$mFh{%P|`7Y<{Ga>VGJRJ%>eS&vgD{K%AZsPcYh zR}~?4Sn|7?H;%kfyI~CMyewJu8rWrh+pR0NYq-Iyq&?|pGN4B-TYzWrbyp>!A<)38 z4%1Lg&0jg5u$U@}6z(^3tDDy`9}Qvpeg^x)O5ZI8it`!f*`5Kvgjs4Q##u&}*=DiI zn!gK5ddr7~KTeHsx+V>5JIb!U`qpgU)F>16$TyDG#KhQ%jGy|+YU~O=2kCWLwf7N0 zX%Sbo>AZn%B0h?8<4)6dI~>r`P6LmtinE+;OU)O|Wex9p(ws2hCJo+9_pFiB z7fAm7$^ThSDziqaaV4p!zZcH?1IeFP&zt$b9G{haDnBg97{BIqH&x|~TRr{mNtgF1 z=KD*IKX7d`AHJ*>xJ?0B3;EYPe-a3nrJ&`HtY_0RBG!7dK>bgh3!%qzVTg$*y+MY) z&$Z8|T}^|k`2EED{c_d(304<g-Im@iW9sI{w z4KPkbki~?=Wtp>|bG;b~`LW$2ht1I_SHKB9W3!PrEptc|R1Yy; zL@g}P*5dF&^1AI!S0rw>;(g?>MWY)#lX^iM*AL>3H>kVuE63I@@!OM<{gtNgl29Y= z$^`X5{qhOWJ2HWP{AE~d%@UaqJ_L+dy2JO@AXYtb^96==Ut?GO`W&^~Le97!;K3a` z=?l=}772&XuzBVWt*y=8MfJjUx6iG4BNV{th8^B9N^x2wm{R^G=X52#O9B1MWoAC@ zp1xqHET!2Hy`ewqcZvU%1Frz-;E~+EAuqN@lnWGN)N|X2^itS8+MkaTv9oe9ldamr zpS2{JVzL@2Bvi}=Nii~+uY+@+OR18n)q#s5o=uczm)Acyy)PdGb)79|j7*YoAE07> zR`N%M{!UWAahsr!##wZg{a)X~h#T|vN!gy2`{vaiqcq`c)c|6%UCUNfJ1>%-T5l-b zp=s4{-N=IPAX0nM)8>w{-~|ginZhrAq)NOaoJC7sHpFg9jZ>KDYsR~;OtGzR=ai>k zg2qili0Iyw1YyE!f{P65K)rcr%JX_h-u#w=ZyG}lmZ{`U9e*Yg_e>8T-4(kX%B^`0 z9PO7HKCu0@&@-2{$|71S(wKBumamE%t#&9d$rL>_dPFn^H(cc8$D0dwetT0u~E zav=YHx$|t?RjAix^BY5*cG-M6dW?aGScPcvZ1WaFRz*emZ-6;-XsdYu;M#$K_??%U%)qP1NrdiQm%Bp9X&p zGZHi1mxeT_v*>(tPAI0(&%1qh5mXmP^7eAOwT`x=6+ZQ$d5AG{+wcW$S6i*&3H$Wv z`5{Ce0)`Ock(HgCm*wo&uHc@KVUNx#p)sG-brDlzk#;QGPN23U_sZnaC zR#j1}wMX*web4(o&w2j1=brQV-h2PM9~gp(p8`k;Lom)ksBa_nkjL~uC@>$vI0Qm4 z_Wi}(Fa+4~zap?H2m!wT7wi2kEeP%ZZPC#HDe@7Za~J}247&xP`G5TPI2VXTfN24L z92dsPc*Zj?PJcd1XKPtOK?&&odkfTG#4NWoXi#X?tUX=j%hxaKOs%{99O_IITApR-b2_+u8z$_({pQY9E^_?lJ%Lxw)um4Y z-}4W>TyaKDjqM}}SAe&dg!VeC))bT0_r9A1KjddPnUuKfB_uzsCr>@`W|jzm8wBi2 zg=(C()RGx{{q}{Q*NNO`NfbhXNhfvNgjG)=UuV6*rfXjdScE%>O$|(JLRo~IFRRW% zr}yaEYS^Tuo`*AtuhWGtJ#HnT)zEYf^}pIGyRIgjErR?Qfu^(1j~q`k#9&0P1J9@@ z+hmK$nBT1C7pq7p0U(d2`Hy$5*ci2TO??+zigDpIlV(MW*HSbDr(BCjwn^iy=3YG%{5?~1 z^mA6vDK5$6&1ZM_34*mMsFP65k~#!S6(tAfkBZePTezHVEr=amkhsr5i)<11Gae3Bk^T$|$j+p(xtwof(Xr?DzJhNeGCK{Hl z1;xDO3K9fydzRy!0vKMWfQzli*-XXh7!?(~db&$SCNz7Ysi~}Qj>6u>BRGBE>hk>o zA>Jeq>JuvwDWlVb9h`TLxyGaxC1jpc*-a$F+u$wXlPRVh_fcdip7d$I-q~^ z?fYu@fv%z5#p2d!aotq_y`$v^(6ArP zx-9SL&%-b}Ql1h~x=oQw%-#G`i&4h+`%Xd3!zCj-x1a|9^prRoHXtI#m=xW(OERBL zPv=BdZqLjFI*yH_`x?H@HNwpT#K%fevou_WFK>Pj?dlMLK!hm&>@$W4iaf^kdd3ln z?~(?y6qsAWGL3ZOUFMY7e|57l6z|<0bDrk2YA*2%RRS1jTRdRBQy6KvKJ{y9>L#>k zVe_R@KT!{ryp3h5xQ>lIdY#8u9#k!MVfN=?UW`dCQiT`e^eqL1Ly=0~E&(%-jkf`* zUXcv0b@fFxl|xAUNCvd*Wyio#b%{!vFXYU1>%`BTJbW=#*C`yJ%E6JaE`_==&c81! zAq%_mZ&gyWNPu`L01`0%{~%Tg7SL5oFF!Xv2^j?md3k9WSxH%WSr~%G?%zXDNIrtb z=r7Wz00RCtjsGOT|MDv2f4qtj_c{!@r!sE|pJP>kj#x?dQuh9@SECR#ROR4)eD!xU zmF_n!<@W?u(uurVa|N;uO#2=|H+w7BytQ>HQ|Mn<(PdtX=Jo9<$0|6?qa0t^Ef+Jv?(Axl<-+C456ved zR14F?S`FFst?(9SG0(4*s|4aeA0jKukSBwW*0|8l)UfI!+RNx&_J zz2Rb1E<0TAmx|C@^AwfqZOo=Ow|tLZlYCH(Ocy@07fV=?AX1A}sM@So2n-ejNh{za zNdeN7VkVA2ca!ndk`Nr)fR*pgHp!TEt=18Ra$N`q_!d6t(!Q;-S+@@qXrBcC2tP?I zcY78bkD5Y@aZ1tLOoOFg-2?z!&6as`)H5}FfQNWrff#?8`c6OXBkQtzsrFbxc-u{- z%U+3{K=xegE17<_a`svc_Br0Q(KMUyFw++~>qr~xdrcfON?KX5x-cb6)3L#_umt``m+@GX3x#xk76gtdGdFU@;+FUCW*8N4U|VRut3 zA#=yPdh=+=7pIitM}mgP?uxw7ewn7|6JDSl!$h?+v>F>AwbFSj@tS#X>b0uvS&i`> zRpqQ1^oAmfL)E4L73r+7qcU7ZAyF^eu{3YtpzQ}?P0hSy(nYM5bv|d zeoZ{s{UUAEwI7b9?@KIaaU|3O6Vua-BOI@k`u(oWI=vV^_gyq;eTP&q=f+3M<=1>) z<9+}S?FkPM$UL!}Cr=b%*f^6DX=n;l{kcm46ETz;kNr49ySXy)KFaNWo|Z8RT6iL|fI z7`o{Dxz^=Sa&4|ubn)RcGwn_YvMkf^L=<7&BhwDD-e~*xnXSP&1=wZUN_E@ci=C}iX+uXZheqa; zrwmARHL0=K%8NS=#f9TIi)~Lv51*%d^L=Jf>=GesJIa&_E2Q9eBk<_PP1Y~ySWT8I z{wR{5Cya~H{jSg;2CZ3YrxWV!V9n!;K+IVS@^A(OaqCk_=VgCOpTZ*{+$0M4c)d*R z17QO=_jXw$^$k@Dg>iilz)Edw)<*ksVmjSDA2V`XzciGfm>$i$GizT{zNDr)J=`I5 z#%>GrHxH%9-Bfy~U*f9^ojKA_W`lF`m zT2u5w7e3aU^rp||+QhMau98H14r5+6*Osvvh4DLWDgOQ5pK9);@O|+8H5oP5y)pVD zzRAPucT{}O9n#W6MlQLf+^J8b;Uboj#x8!3>AitD!nA{4Du_;@X2;Vb6*Rm z!)>z%a9Jt8`d=pZ!qw>vk=f>*R$nWtiQhBE3q1C6pTsk7q~SLcDXMz#S zCu|Q=1c6!Lf+=@2BLh2p`S_U6-8{}i)<-G}Le|LP7EJN(M1qF&j~M2EtZf(<`pl^T z0_lAsz4NN&03~9IKSaS;4CboG(4!m!uBpn)OL^TI!xOe#=E@!XKtS)j#Z#_chPypbCeM1p|8}V{N)?udQ~g4u2_y-OtCbBlEm@ z=B;BQ+MZi03LuiTU^n4fx8UVJgNEJq#)AdR;# zD=Yn+s18l|A;ir*P@;xz)FW-Gdl^sZ22MO-{>mhE_L-O>4+rNC-f$eHCyQd-c_2E8 z56)%*A|^cJWLli}5?N^X8T`8uw&}pDC9eDaI<%jCPM4S>{iUUk0ko-xJ2Z)Pmu_;p zDDVG>csN>oSMEWhNN58}CFopuPuu+ZK)g*N6rTN%m_mgdGU=Y5?7$hZ6S4&Vh^6ij zKZ;Te%gjZdvA2~6Oesu`GqY^i2fQY~0y6b569&#KZcq);iCGs_V5I{m011&}CL>Zt zN54kE>8k|OvjU3oxNBqD(6ivJ%H|W6OsvIP*MtcheTyTG3RDmtjPN@k9+R<92h1Rz zNp7nYRyVxyT`f_o!13 zROazdppozUl0TfnRtGJb8jqP!CA%c{d7Lx%wJt1$Wt6(`*O6Uw z_nExHrAOA#ou8}BLOi9h953j3I}f>3qvRgmMsij4`!FIr2(?hnL#MT#aD}l()@Wn! zIwrc8$%!QS>Q$&Dz)wKXW#RH&;ku6d;0EE$O!32LHt(}9(#subeacy90=p%`om%M# zPP3|&r?iqDXM@vUA8^OpE_`UQ99#&UG_X1cG@ksC!23|we$FP^QMU0&O-M_XjY|3N z$0!;s)c!te%hfR7Aft2m5GJ$5Bj~y>j6iarDu!pGb7O`+;=N&JtE1I7ot?2ah@z=h zW2K$ZPl4%!zuON?jp_ux)7X81p9@7<=7o)muQ!`VU*(U_GMZtu2O?fDpn4;&s;Q(e zRGDC(^{49^Z4ZPC^eY6YNJjucoEOc70koBr&K)(r+s?@g^Se%SH`l9uWoVU9k8~!7 zSm(Da`qf`K>*l$GR3fX#>*?3KSLGipLS>5eZQT02fellR^9>>##$oNHE}<85p%~Q7 z%_(}1jZoEc^nl$(=_+s4AiZQK^f5uJuh zyx{G`iKeIyY%fRaL#e4KJl~qBe?H-g4xGk|wmuFpt&b6U)UaK7xbX7Trn9x!O|I0d zM0C&$Crky#52J>FMHwh5IKcoA3);!)%GXyMs9+@{1qF^WmEsdjJ%}afv*WKBoMu}7 U@+;F6i_J(6+nRj}N+2@-U-0Veh5!Hn literal 0 HcmV?d00001 diff --git a/webclient/tests/pom.xml b/webclient/tests/pom.xml index b4a829ffef0..a8febb0986d 100644 --- a/webclient/tests/pom.xml +++ b/webclient/tests/pom.xml @@ -37,6 +37,7 @@ http1 http2 webclient + grpc diff --git a/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClient.java b/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClient.java index 1070e29ba50..b382572585c 100644 --- a/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClient.java +++ b/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClient.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. @@ -31,7 +31,7 @@ @RuntimeType.PrototypedBy(WsClientConfig.class) public interface WsClient extends RuntimeType.Api { /** - * Protocol to use to obtain an instance of WebSocket specific clietn from + * Protocol to use to obtain an instance of WebSocket specific client from * {@link io.helidon.webclient.api.WebClient#client(io.helidon.webclient.spi.Protocol)}. */ Protocol PROTOCOL = WsProtocolProvider::new; diff --git a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/Grpc.java b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/Grpc.java index 0dcc498eaea..7ed1caedd9f 100644 --- a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/Grpc.java +++ b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/Grpc.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. @@ -136,17 +136,17 @@ private static Grpc grpc(Descriptors.FileDescriptor pro - to invoke a static method on it */ Class requestType = load(getClassName(mtd.getInputType())); - Class responsetype = load(getClassName(mtd.getOutputType())); + Class responseType = load(getClassName(mtd.getOutputType())); MethodDescriptor.Marshaller reqMarshaller = ProtoMarshaller.get(requestType); - MethodDescriptor.Marshaller resMarshaller = ProtoMarshaller.get(responsetype); + MethodDescriptor.Marshaller resMarshaller = ProtoMarshaller.get(responseType); io.grpc.MethodDescriptor.Builder grpcDesc = io.grpc.MethodDescriptor.newBuilder() .setFullMethodName(io.grpc.MethodDescriptor.generateFullMethodName(serviceName, methodName)) .setType(getMethodType(mtd)).setFullMethodName(path).setRequestMarshaller(reqMarshaller) .setResponseMarshaller(resMarshaller).setSampledToLocalTracing(true); - return new Grpc<>(grpcDesc.build(), PathMatchers.exact(path), requestType, responsetype, callHandler); + return new Grpc<>(grpcDesc.build(), PathMatchers.exact(path), requestType, responseType, callHandler); } diff --git a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcProtocolSelector.java b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcProtocolSelector.java index 3c7d108a0ad..806e08bf767 100644 --- a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcProtocolSelector.java +++ b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcProtocolSelector.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. @@ -65,7 +65,7 @@ public SubProtocolResult subProtocol(ConnectionContext ctx, Headers httpHeaders = headers.httpHeaders(); if (httpHeaders.contains(HeaderNames.CONTENT_TYPE)) { - String contentType = httpHeaders.get(HeaderNames.CONTENT_TYPE).value(); + String contentType = httpHeaders.get(HeaderNames.CONTENT_TYPE).get(); if (contentType.startsWith("application/grpc")) { GrpcRouting routing = router.routing(GrpcRouting.class, GrpcRouting.empty()); diff --git a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcService.java b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcService.java index 59581ae076b..50926ebd66e 100644 --- a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcService.java +++ b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcService.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/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcServiceRoute.java b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcServiceRoute.java index f90927009fd..fdd0d91e76b 100644 --- a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcServiceRoute.java +++ b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcServiceRoute.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/webserver/testing/junit5/grpc/pom.xml b/webserver/testing/junit5/grpc/pom.xml new file mode 100644 index 00000000000..92a63e7f6c8 --- /dev/null +++ b/webserver/testing/junit5/grpc/pom.xml @@ -0,0 +1,57 @@ + + + + + 4.0.0 + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5-project + 4.0.0-SNAPSHOT + + + helidon-webserver-testing-junit5-grpc + Helidon WebServer Testing JUnit5 gRPC + + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5 + + + io.helidon.webclient + helidon-webclient-grpc + + + io.helidon.webserver + helidon-webserver-grpc + + + org.junit.jupiter + junit-jupiter-api + provided + + + org.hamcrest + hamcrest-all + provided + + + diff --git a/webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/GrpcServerExtension.java b/webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/GrpcServerExtension.java new file mode 100644 index 00000000000..372e35a6ac9 --- /dev/null +++ b/webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/GrpcServerExtension.java @@ -0,0 +1,87 @@ +/* + * 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.webserver.testing.junit5.grpc; + +import java.util.Optional; + +import io.helidon.webclient.grpc.GrpcClient; +import io.helidon.webserver.ListenerConfig; +import io.helidon.webserver.Router; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.grpc.GrpcRouting; +import io.helidon.webserver.testing.junit5.Junit5Util; +import io.helidon.webserver.testing.junit5.spi.ServerJunitExtension; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; + +/** + * A {@link java.util.ServiceLoader} provider implementation that adds support for injection of gRPC related + * artifacts, such as {@link io.helidon.webclient.grpc.GrpcClient} in Helidon integration tests. + */ +public class GrpcServerExtension implements ServerJunitExtension { + + @Override + public Optional> setUpRouteParamHandler(Class type) { + if (GrpcRouting.Builder.class.equals(type)) { + return Optional.of(new RoutingParamHandler()); + } + return Optional.empty(); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return GrpcClient.class.equals(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext, + Class parameterType, + WebServer server) { + String socketName = Junit5Util.socketName(parameterContext.getParameter()); + + if (GrpcClient.class.equals(parameterType)) { + return GrpcClient.builder() + .baseUri("http://localhost:" + server.port(socketName)) + .build(); + } + throw new ParameterResolutionException("gRPC extension only supports GrpcClient parameter type"); + } + + private static final class RoutingParamHandler implements ParamHandler { + @Override + public GrpcRouting.Builder get(String socketName, + WebServerConfig.Builder serverBuilder, + ListenerConfig.Builder listenerBuilder, + Router.RouterBuilder routerBuilder) { + return GrpcRouting.builder(); + } + + @Override + public void handle(String socketName, + WebServerConfig.Builder serverBuilder, + ListenerConfig.Builder listenerBuilder, + Router.RouterBuilder routerBuilder, + GrpcRouting.Builder value) { + routerBuilder.addRouting(value); + } + } +} diff --git a/webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/package-info.java b/webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/package-info.java new file mode 100644 index 00000000000..cc425f5e775 --- /dev/null +++ b/webserver/testing/junit5/grpc/src/main/java/io/helidon/webserver/testing/junit5/grpc/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 WebServer Testing JUnit 5 Support for gRPC. + */ +package io.helidon.webserver.testing.junit5.grpc; diff --git a/webserver/testing/junit5/grpc/src/main/java/module-info.java b/webserver/testing/junit5/grpc/src/main/java/module-info.java new file mode 100644 index 00000000000..81f02a3db8a --- /dev/null +++ b/webserver/testing/junit5/grpc/src/main/java/module-info.java @@ -0,0 +1,32 @@ +/* + * 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 WebServer Testing JUnit 5 Support for gRPC. + */ +module io.helidon.webserver.testing.junit5.grpc { + + requires io.helidon.webclient.grpc; + requires io.helidon.webserver.grpc; + + requires transitive io.helidon.webserver.testing.junit5; + + exports io.helidon.webserver.testing.junit5.grpc; + + provides io.helidon.webserver.testing.junit5.spi.ServerJunitExtension + with io.helidon.webserver.testing.junit5.grpc.GrpcServerExtension; + +} \ No newline at end of file diff --git a/webserver/testing/junit5/pom.xml b/webserver/testing/junit5/pom.xml index 00e10496c9f..0994ebe57cb 100644 --- a/webserver/testing/junit5/pom.xml +++ b/webserver/testing/junit5/pom.xml @@ -37,6 +37,7 @@ junit5 websocket http2 + grpc From b5ee94f05e8fc663559b6f49362523bfa342d6f6 Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Thu, 30 May 2024 09:23:20 -0700 Subject: [PATCH 032/245] 4.x: Bump version to 4.1.0-SNAPSHOT. Update CHANGELOG (#8812) * Update CHANGELOG for 4.0.9 * Update version to 4.1.0-SNAPSHOT --- CHANGELOG.md | 46 + all-config-meta.json | 5804 +++++++++++++++++ all/pom.xml | 2 +- applications/mp/pom.xml | 2 +- applications/parent/pom.xml | 2 +- applications/pom.xml | 2 +- applications/se/pom.xml | 2 +- archetypes/archetypes/pom.xml | 2 +- archetypes/legacy/bare-mp/pom.xml | 2 +- archetypes/legacy/bare-se/pom.xml | 2 +- archetypes/legacy/database-mp/pom.xml | 2 +- archetypes/legacy/database-se/pom.xml | 2 +- archetypes/legacy/pom.xml | 2 +- archetypes/legacy/quickstart-mp/pom.xml | 2 +- archetypes/legacy/quickstart-se/pom.xml | 2 +- archetypes/pom.xml | 2 +- bom/pom.xml | 4 +- builder/api/pom.xml | 2 +- builder/codegen/pom.xml | 2 +- builder/pom.xml | 2 +- builder/processor/pom.xml | 2 +- builder/tests/builder/pom.xml | 2 +- builder/tests/codegen/pom.xml | 2 +- builder/tests/common-types/pom.xml | 2 +- builder/tests/pom.xml | 2 +- bundles/config/pom.xml | 2 +- bundles/pom.xml | 2 +- bundles/security/pom.xml | 2 +- codegen/apt/pom.xml | 2 +- codegen/class-model/pom.xml | 2 +- codegen/codegen/pom.xml | 2 +- codegen/compiler/pom.xml | 2 +- codegen/helidon-copyright/pom.xml | 2 +- codegen/pom.xml | 2 +- codegen/scan/pom.xml | 2 +- common/buffers/pom.xml | 2 +- common/common/pom.xml | 2 +- common/config/pom.xml | 2 +- common/configurable/pom.xml | 2 +- common/context/pom.xml | 2 +- common/crypto/pom.xml | 2 +- common/features/api/pom.xml | 2 +- common/features/features/pom.xml | 2 +- common/features/pom.xml | 2 +- common/features/processor/pom.xml | 2 +- common/key-util/pom.xml | 2 +- common/mapper/pom.xml | 2 +- common/media-type/pom.xml | 2 +- common/parameters/pom.xml | 2 +- common/pom.xml | 2 +- common/processor/class-model/pom.xml | 2 +- common/processor/helidon-copyright/pom.xml | 2 +- common/processor/pom.xml | 2 +- common/processor/processor/pom.xml | 2 +- common/reactive/pom.xml | 2 +- common/security/pom.xml | 2 +- common/socket/pom.xml | 2 +- common/task/pom.xml | 2 +- common/testing/http-junit5/pom.xml | 2 +- common/testing/junit5/pom.xml | 2 +- common/testing/pom.xml | 2 +- common/tls/pom.xml | 2 +- common/types/pom.xml | 2 +- common/uri/pom.xml | 2 +- config/config-mp/pom.xml | 2 +- config/config/pom.xml | 2 +- config/encryption/pom.xml | 2 +- config/etcd/pom.xml | 2 +- config/git/pom.xml | 2 +- config/hocon-mp/pom.xml | 2 +- config/hocon/pom.xml | 2 +- config/metadata-processor/pom.xml | 2 +- config/metadata/pom.xml | 2 +- config/object-mapping/pom.xml | 2 +- config/pom.xml | 2 +- config/testing/pom.xml | 2 +- .../tests/config-metadata-builder-api/pom.xml | 2 +- config/tests/config-metadata-meta-api/pom.xml | 2 +- config/tests/integration-tests/pom.xml | 2 +- config/tests/module-mappers-1-base/pom.xml | 2 +- .../tests/module-mappers-2-override/pom.xml | 2 +- config/tests/module-meta-source-1/pom.xml | 2 +- config/tests/module-meta-source-2/pom.xml | 2 +- .../tests/module-parsers-1-override/pom.xml | 2 +- config/tests/pom.xml | 2 +- config/tests/service-registry/pom.xml | 2 +- config/tests/test-bundle/pom.xml | 2 +- .../test-default_config-1-properties/pom.xml | 2 +- .../test-default_config-2-hocon-json/pom.xml | 2 +- .../tests/test-default_config-3-hocon/pom.xml | 2 +- .../tests/test-default_config-4-yaml/pom.xml | 2 +- .../test-default_config-5-env_vars/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../test-default_config-8-meta-hocon/pom.xml | 2 +- .../test-default_config-9-meta-yaml/pom.xml | 2 +- config/tests/test-lazy-source/pom.xml | 2 +- config/tests/test-mappers-1-common/pom.xml | 2 +- config/tests/test-mappers-2-complex/pom.xml | 2 +- config/tests/test-meta-source/pom.xml | 2 +- config/tests/test-parsers-1-complex/pom.xml | 2 +- config/yaml-mp/pom.xml | 2 +- config/yaml/pom.xml | 2 +- cors/pom.xml | 2 +- dbclient/dbclient/pom.xml | 2 +- dbclient/health/pom.xml | 2 +- dbclient/hikari/pom.xml | 2 +- dbclient/jdbc/pom.xml | 2 +- dbclient/jsonp/pom.xml | 2 +- dbclient/metrics-hikari/pom.xml | 2 +- dbclient/metrics/pom.xml | 2 +- dbclient/mongodb/pom.xml | 2 +- dbclient/pom.xml | 2 +- dbclient/tracing/pom.xml | 2 +- dependencies/pom.xml | 2 +- docs/pom.xml | 2 +- .../main/asciidoc/includes/attributes.adoc | 4 +- examples/config/basics/pom.xml | 2 +- examples/config/changes/pom.xml | 2 +- examples/config/git/pom.xml | 2 +- examples/config/mapping/pom.xml | 2 +- examples/config/metadata/pom.xml | 2 +- examples/config/overrides/pom.xml | 2 +- examples/config/pom.xml | 2 +- examples/config/profiles/pom.xml | 2 +- examples/config/sources/pom.xml | 2 +- examples/cors/pom.xml | 2 +- examples/dbclient/common/pom.xml | 2 +- examples/dbclient/jdbc/pom.xml | 2 +- examples/dbclient/mongodb/pom.xml | 2 +- examples/dbclient/pokemons/pom.xml | 2 +- examples/dbclient/pom.xml | 2 +- examples/employee-app/pom.xml | 2 +- examples/graphql/basics/pom.xml | 2 +- examples/graphql/pom.xml | 2 +- examples/health/basics/pom.xml | 2 +- examples/health/pom.xml | 2 +- .../cdi/datasource-hikaricp-h2/pom.xml | 2 +- .../cdi/datasource-hikaricp-mysql/pom.xml | 2 +- .../cdi/datasource-hikaricp/pom.xml | 2 +- examples/integrations/cdi/jpa/pom.xml | 2 +- examples/integrations/cdi/pokemons/pom.xml | 2 +- examples/integrations/cdi/pom.xml | 2 +- examples/integrations/micrometer/mp/pom.xml | 2 +- examples/integrations/micrometer/pom.xml | 2 +- examples/integrations/micrometer/se/pom.xml | 2 +- examples/integrations/micronaut/data/pom.xml | 2 +- examples/integrations/micronaut/pom.xml | 2 +- .../microstream/greetings-mp/pom.xml | 2 +- .../microstream/greetings-se/pom.xml | 2 +- examples/integrations/microstream/pom.xml | 2 +- examples/integrations/neo4j/pom.xml | 2 +- examples/integrations/oci/atp-cdi/pom.xml | 2 +- examples/integrations/oci/atp/pom.xml | 2 +- examples/integrations/oci/metrics/pom.xml | 2 +- .../oci/objectstorage-cdi/pom.xml | 2 +- .../integrations/oci/objectstorage/pom.xml | 2 +- examples/integrations/oci/pom.xml | 2 +- examples/integrations/oci/vault-cdi/pom.xml | 2 +- examples/integrations/oci/vault/pom.xml | 2 +- examples/integrations/pom.xml | 2 +- examples/integrations/vault/hcp-cdi/pom.xml | 2 +- examples/integrations/vault/hcp/pom.xml | 2 +- examples/integrations/vault/pom.xml | 2 +- examples/jbatch/pom.xml | 2 +- examples/logging/jul/pom.xml | 2 +- examples/logging/log4j/pom.xml | 2 +- examples/logging/logback-aot/pom.xml | 2 +- examples/logging/pom.xml | 2 +- examples/logging/slf4j/pom.xml | 2 +- examples/media/multipart/pom.xml | 2 +- examples/media/pom.xml | 2 +- examples/messaging/jms-websocket-mp/pom.xml | 2 +- examples/messaging/jms-websocket-se/pom.xml | 2 +- examples/messaging/kafka-websocket-mp/pom.xml | 2 +- examples/messaging/kafka-websocket-se/pom.xml | 2 +- .../messaging/oracle-aq-websocket-mp/pom.xml | 2 +- examples/messaging/pom.xml | 2 +- examples/messaging/weblogic-jms-mp/pom.xml | 2 +- examples/metrics/exemplar/pom.xml | 2 +- examples/metrics/filtering/mp/pom.xml | 2 +- examples/metrics/filtering/pom.xml | 2 +- examples/metrics/filtering/se/pom.xml | 2 +- examples/metrics/http-status-count-se/pom.xml | 2 +- examples/metrics/kpi/pom.xml | 2 +- examples/metrics/pom.xml | 2 +- examples/microprofile/bean-validation/pom.xml | 2 +- examples/microprofile/cors/pom.xml | 2 +- examples/microprofile/graphql/pom.xml | 2 +- .../microprofile/hello-world-explicit/pom.xml | 2 +- .../microprofile/hello-world-implicit/pom.xml | 2 +- .../microprofile/http-status-count-mp/pom.xml | 2 +- examples/microprofile/idcs/pom.xml | 2 +- examples/microprofile/lra/pom.xml | 2 +- examples/microprofile/messaging-sse/pom.xml | 2 +- examples/microprofile/multipart/pom.xml | 2 +- examples/microprofile/multiport/pom.xml | 2 +- .../microprofile/oci-tls-certificates/pom.xml | 2 +- examples/microprofile/oidc/pom.xml | 2 +- examples/microprofile/openapi/pom.xml | 2 +- examples/microprofile/pom.xml | 2 +- examples/microprofile/security/pom.xml | 2 +- examples/microprofile/static-content/pom.xml | 2 +- .../microprofile/telemetry/greeting/pom.xml | 2 +- examples/microprofile/telemetry/pom.xml | 2 +- .../microprofile/telemetry/secondary/pom.xml | 2 +- examples/microprofile/tls/pom.xml | 2 +- examples/microprofile/websocket/pom.xml | 2 +- examples/openapi-tools/pom.xml | 2 +- examples/openapi/pom.xml | 2 +- examples/pom.xml | 2 +- .../helidon-quickstart-mp/build.gradle | 2 +- .../quickstarts/helidon-quickstart-mp/pom.xml | 2 +- .../helidon-quickstart-se/build.gradle | 2 +- .../quickstarts/helidon-quickstart-se/pom.xml | 4 +- .../helidon-standalone-quickstart-mp/pom.xml | 4 +- .../helidon-standalone-quickstart-se/pom.xml | 4 +- examples/quickstarts/pom.xml | 2 +- .../attribute-based-access-control/pom.xml | 2 +- .../basic-auth-with-static-content/pom.xml | 2 +- examples/security/google-login/pom.xml | 2 +- examples/security/idcs-login/pom.xml | 2 +- examples/security/outbound-override/pom.xml | 2 +- examples/security/pom.xml | 2 +- examples/security/programmatic/pom.xml | 2 +- examples/security/spi-examples/pom.xml | 2 +- examples/security/vaults/pom.xml | 2 +- .../security/webserver-digest-auth/pom.xml | 2 +- .../security/webserver-signatures/pom.xml | 2 +- examples/todo-app/backend/pom.xml | 2 +- examples/todo-app/frontend/pom.xml | 2 +- examples/todo-app/pom.xml | 4 +- examples/translator-app/backend/pom.xml | 2 +- examples/translator-app/frontend/pom.xml | 2 +- examples/translator-app/pom.xml | 2 +- examples/webclient/pom.xml | 2 +- examples/webclient/standalone/pom.xml | 2 +- examples/webserver/basic/pom.xml | 2 +- examples/webserver/basics/pom.xml | 2 +- examples/webserver/comment-aas/pom.xml | 2 +- examples/webserver/echo/pom.xml | 2 +- examples/webserver/fault-tolerance/pom.xml | 2 +- examples/webserver/imperative/pom.xml | 2 +- examples/webserver/multiport/pom.xml | 2 +- examples/webserver/mutual-tls/pom.xml | 2 +- examples/webserver/observe/pom.xml | 2 +- examples/webserver/opentracing/pom.xml | 2 +- examples/webserver/pom.xml | 2 +- examples/webserver/protocols/pom.xml | 2 +- examples/webserver/static-content/pom.xml | 2 +- examples/webserver/streaming/pom.xml | 2 +- examples/webserver/tls/pom.xml | 2 +- examples/webserver/tracing/pom.xml | 2 +- examples/webserver/tutorial/pom.xml | 2 +- examples/webserver/websocket/pom.xml | 2 +- fault-tolerance/fault-tolerance/pom.xml | 2 +- fault-tolerance/pom.xml | 2 +- graphql/pom.xml | 2 +- graphql/server/pom.xml | 2 +- health/health-checks/pom.xml | 2 +- health/health/pom.xml | 2 +- health/pom.xml | 2 +- helidon/pom.xml | 2 +- http/encoding/deflate/pom.xml | 2 +- http/encoding/encoding/pom.xml | 2 +- http/encoding/gzip/pom.xml | 2 +- http/encoding/pom.xml | 2 +- http/http/pom.xml | 2 +- http/http2/pom.xml | 2 +- http/media/jackson/pom.xml | 2 +- http/media/jsonb/pom.xml | 2 +- http/media/jsonp/pom.xml | 2 +- http/media/media/pom.xml | 2 +- http/media/multipart/pom.xml | 2 +- http/media/pom.xml | 2 +- http/pom.xml | 2 +- http/sse/pom.xml | 2 +- http/tests/encoding/deflate/pom.xml | 2 +- http/tests/encoding/gzip/pom.xml | 2 +- http/tests/encoding/pom.xml | 2 +- http/tests/media/jsonb/pom.xml | 2 +- http/tests/media/jsonp/pom.xml | 2 +- http/tests/media/multipart/pom.xml | 2 +- http/tests/media/pom.xml | 2 +- http/tests/media/string/pom.xml | 2 +- http/tests/pom.xml | 2 +- inject/api/pom.xml | 2 +- inject/configdriven/api/pom.xml | 2 +- inject/configdriven/pom.xml | 2 +- inject/configdriven/processor/pom.xml | 2 +- inject/configdriven/runtime/pom.xml | 2 +- inject/configdriven/tests/config/pom.xml | 2 +- .../tests/configuredby-application/pom.xml | 2 +- .../configdriven/tests/configuredby/pom.xml | 2 +- inject/configdriven/tests/pom.xml | 2 +- inject/maven-plugin/pom.xml | 2 +- inject/pom.xml | 2 +- inject/processor/pom.xml | 2 +- inject/runtime/pom.xml | 2 +- inject/testing/pom.xml | 2 +- inject/tests/api/pom.xml | 2 +- inject/tests/interception/pom.xml | 2 +- inject/tests/pom.xml | 2 +- inject/tests/resources-inject/pom.xml | 2 +- inject/tests/resources-plain/pom.xml | 2 +- inject/tests/runtime/pom.xml | 2 +- inject/tests/tck-jsr330/pom.xml | 2 +- inject/tools/pom.xml | 2 +- .../cdi/common-cdi/configurable/pom.xml | 2 +- integrations/cdi/common-cdi/delegates/pom.xml | 2 +- integrations/cdi/common-cdi/pom.xml | 2 +- .../reference-counted-context/pom.xml | 2 +- integrations/cdi/datasource-hikaricp/pom.xml | 2 +- integrations/cdi/datasource-ucp/pom.xml | 2 +- integrations/cdi/datasource/pom.xml | 2 +- integrations/cdi/eclipselink-cdi/pom.xml | 2 +- integrations/cdi/hibernate-cdi/pom.xml | 2 +- integrations/cdi/jpa-cdi/pom.xml | 2 +- integrations/cdi/jta-cdi/pom.xml | 2 +- integrations/cdi/jta-weld/pom.xml | 2 +- integrations/cdi/pom.xml | 2 +- integrations/common/pom.xml | 2 +- integrations/common/rest/pom.xml | 2 +- integrations/db/h2/pom.xml | 2 +- integrations/db/mysql/pom.xml | 2 +- integrations/db/ojdbc/pom.xml | 2 +- integrations/db/pgsql/pom.xml | 2 +- integrations/db/pom.xml | 2 +- .../graal/mp-native-image-extension/pom.xml | 2 +- .../graal/native-image-extension/pom.xml | 2 +- integrations/graal/pom.xml | 2 +- integrations/jdbc/jdbc/pom.xml | 2 +- integrations/jdbc/pom.xml | 2 +- integrations/jta/jdbc/pom.xml | 2 +- integrations/jta/pom.xml | 2 +- integrations/micrometer/cdi/pom.xml | 2 +- integrations/micrometer/micrometer/pom.xml | 2 +- integrations/micrometer/pom.xml | 2 +- integrations/micronaut/cdi-processor/pom.xml | 2 +- integrations/micronaut/cdi/pom.xml | 2 +- integrations/micronaut/data/pom.xml | 2 +- integrations/micronaut/pom.xml | 2 +- integrations/microstream/cache/pom.xml | 2 +- integrations/microstream/cdi/pom.xml | 2 +- integrations/microstream/core/pom.xml | 2 +- integrations/microstream/health/pom.xml | 2 +- integrations/microstream/metrics/pom.xml | 2 +- integrations/microstream/pom.xml | 2 +- integrations/neo4j/health/pom.xml | 2 +- integrations/neo4j/metrics/pom.xml | 2 +- integrations/neo4j/neo4j/pom.xml | 2 +- integrations/neo4j/pom.xml | 2 +- integrations/oci/metrics/cdi/pom.xml | 2 +- integrations/oci/metrics/metrics/pom.xml | 2 +- integrations/oci/metrics/pom.xml | 2 +- integrations/oci/oci/pom.xml | 2 +- integrations/oci/pom.xml | 2 +- integrations/oci/sdk/cdi/pom.xml | 2 +- integrations/oci/sdk/pom.xml | 2 +- integrations/oci/sdk/processor/pom.xml | 2 +- integrations/oci/sdk/runtime/pom.xml | 2 +- integrations/oci/sdk/tests/pom.xml | 2 +- .../oci/sdk/tests/test-application/pom.xml | 2 +- .../oci/sdk/tests/test-module1/pom.xml | 2 +- .../oci/sdk/tests/test-module2/pom.xml | 2 +- .../oci/secrets-config-source/pom.xml | 2 +- .../oci/secrets-mp-config-source/pom.xml | 2 +- integrations/oci/tls-certificates/pom.xml | 2 +- integrations/openapi-ui/pom.xml | 2 +- integrations/pom.xml | 2 +- integrations/vault/auths/approle/pom.xml | 2 +- integrations/vault/auths/common/pom.xml | 2 +- integrations/vault/auths/k8s/pom.xml | 2 +- integrations/vault/auths/pom.xml | 2 +- integrations/vault/auths/token/pom.xml | 2 +- integrations/vault/cdi/pom.xml | 2 +- integrations/vault/pom.xml | 2 +- integrations/vault/secrets/cubbyhole/pom.xml | 2 +- integrations/vault/secrets/database/pom.xml | 2 +- integrations/vault/secrets/kv1/pom.xml | 2 +- integrations/vault/secrets/kv2/pom.xml | 2 +- integrations/vault/secrets/pki/pom.xml | 2 +- integrations/vault/secrets/pom.xml | 2 +- integrations/vault/secrets/transit/pom.xml | 2 +- integrations/vault/sys/pom.xml | 2 +- integrations/vault/sys/sys/pom.xml | 2 +- integrations/vault/vault/pom.xml | 2 +- jersey/client/pom.xml | 2 +- jersey/common/pom.xml | 2 +- jersey/connector/pom.xml | 2 +- jersey/jsonp/pom.xml | 2 +- jersey/pom.xml | 2 +- jersey/server/pom.xml | 2 +- jersey/tests/connector/pom.xml | 2 +- jersey/tests/pom.xml | 2 +- licensing/pom.xml | 2 +- logging/common/pom.xml | 2 +- logging/jul/pom.xml | 2 +- logging/log4j/pom.xml | 2 +- logging/pom.xml | 2 +- logging/slf4j/pom.xml | 2 +- logging/tests/log4j/pom.xml | 2 +- logging/tests/pom.xml | 2 +- .../client/narayana-client/pom.xml | 2 +- lra/coordinator/client/pom.xml | 2 +- lra/coordinator/client/spi/pom.xml | 2 +- lra/coordinator/pom.xml | 2 +- lra/coordinator/server/pom.xml | 2 +- lra/pom.xml | 2 +- messaging/connectors/aq/pom.xml | 2 +- messaging/connectors/jms-shim/pom.xml | 2 +- messaging/connectors/jms/pom.xml | 2 +- messaging/connectors/kafka/pom.xml | 2 +- messaging/connectors/mock/pom.xml | 2 +- messaging/connectors/pom.xml | 2 +- messaging/connectors/wls-jms/pom.xml | 2 +- messaging/messaging/pom.xml | 2 +- messaging/pom.xml | 2 +- metrics/api/pom.xml | 2 +- metrics/metrics/pom.xml | 2 +- metrics/pom.xml | 2 +- metrics/prometheus/pom.xml | 2 +- metrics/provider-tests/pom.xml | 2 +- metrics/providers/micrometer/pom.xml | 2 +- metrics/providers/pom.xml | 2 +- metrics/system-meters/pom.xml | 2 +- metrics/trace-exemplar/pom.xml | 2 +- microprofile/access-log/pom.xml | 2 +- microprofile/bean-validation/pom.xml | 2 +- .../bundles/helidon-microprofile-core/pom.xml | 2 +- .../bundles/helidon-microprofile/pom.xml | 2 +- microprofile/bundles/pom.xml | 2 +- microprofile/cdi/pom.xml | 2 +- microprofile/config/pom.xml | 2 +- microprofile/cors/pom.xml | 2 +- microprofile/fault-tolerance/pom.xml | 2 +- microprofile/graphql/pom.xml | 2 +- microprofile/graphql/server/pom.xml | 2 +- microprofile/health/pom.xml | 2 +- microprofile/jwt-auth/pom.xml | 2 +- microprofile/lra/jax-rs/pom.xml | 2 +- microprofile/lra/pom.xml | 2 +- microprofile/messaging/core/pom.xml | 2 +- microprofile/messaging/health/pom.xml | 2 +- microprofile/messaging/metrics/pom.xml | 2 +- microprofile/messaging/pom.xml | 2 +- microprofile/metrics/pom.xml | 2 +- microprofile/oidc/pom.xml | 2 +- microprofile/openapi/pom.xml | 2 +- microprofile/pom.xml | 2 +- microprofile/reactive-streams/pom.xml | 2 +- microprofile/rest-client/pom.xml | 2 +- microprofile/scheduling/pom.xml | 2 +- microprofile/security/pom.xml | 2 +- microprofile/server/pom.xml | 2 +- microprofile/service-common/pom.xml | 2 +- microprofile/telemetry/pom.xml | 2 +- microprofile/testing/junit5/pom.xml | 2 +- microprofile/testing/mocking/pom.xml | 2 +- microprofile/testing/pom.xml | 2 +- microprofile/testing/testng/pom.xml | 2 +- microprofile/tests/arquillian/pom.xml | 2 +- microprofile/tests/config/pom.xml | 2 +- microprofile/tests/pom.xml | 2 +- microprofile/tests/server/pom.xml | 2 +- microprofile/tests/tck/pom.xml | 2 +- .../tests/tck/tck-annotations/pom.xml | 2 +- .../tests/tck/tck-cdi-lang-model/pom.xml | 2 +- microprofile/tests/tck/tck-cdi/pom.xml | 2 +- microprofile/tests/tck/tck-config/pom.xml | 2 +- .../tests/tck/tck-core-profile/pom.xml | 2 +- .../tck-core-profile-test/pom.xml | 2 +- .../tests/tck/tck-fault-tolerance/pom.xml | 2 +- microprofile/tests/tck/tck-graphql/pom.xml | 2 +- microprofile/tests/tck/tck-health/pom.xml | 2 +- microprofile/tests/tck/tck-inject/pom.xml | 2 +- .../tck/tck-inject/tck-inject-test/pom.xml | 2 +- microprofile/tests/tck/tck-jsonb/pom.xml | 2 +- .../tck/tck-jsonb/tck-jsonb-test/pom.xml | 2 +- microprofile/tests/tck/tck-jsonp/pom.xml | 2 +- .../tck-jsonp-pluggability-test/pom.xml | 2 +- .../tck/tck-jsonp/tck-jsonp-test/pom.xml | 2 +- microprofile/tests/tck/tck-jwt-auth/pom.xml | 2 +- microprofile/tests/tck/tck-lra/pom.xml | 2 +- microprofile/tests/tck/tck-messaging/pom.xml | 2 +- microprofile/tests/tck/tck-metrics/pom.xml | 2 +- microprofile/tests/tck/tck-openapi/pom.xml | 2 +- .../tests/tck/tck-opentracing/pom.xml | 2 +- .../tests/tck/tck-reactive-operators/pom.xml | 2 +- .../tests/tck/tck-rest-client/pom.xml | 2 +- microprofile/tests/tck/tck-restful/pom.xml | 2 +- .../tck/tck-restful/tck-restful-test/pom.xml | 2 +- microprofile/tests/tck/tck-telemetry/pom.xml | 2 +- microprofile/tests/telemetry/pom.xml | 2 +- microprofile/tests/testing/junit5/pom.xml | 2 +- microprofile/tests/testing/pom.xml | 4 +- microprofile/tests/testing/testng/pom.xml | 2 +- microprofile/tracing/pom.xml | 2 +- microprofile/websocket/pom.xml | 2 +- microprofile/weld/pom.xml | 2 +- microprofile/weld/weld-core-impl/pom.xml | 2 +- microprofile/weld/weld-se-core/pom.xml | 2 +- openapi/openapi/pom.xml | 2 +- openapi/pom.xml | 2 +- openapi/tests/gh-5792/pom.xml | 2 +- openapi/tests/pom.xml | 2 +- parent/pom.xml | 2 +- pom.xml | 2 +- scheduling/pom.xml | 2 +- security/abac/policy-el/pom.xml | 2 +- security/abac/policy/pom.xml | 2 +- security/abac/pom.xml | 2 +- security/abac/role/pom.xml | 2 +- security/abac/scope/pom.xml | 2 +- security/abac/time/pom.xml | 2 +- security/annotations/pom.xml | 2 +- security/integration/common/pom.xml | 2 +- security/integration/pom.xml | 2 +- security/jwt/pom.xml | 2 +- security/pom.xml | 2 +- security/providers/abac/pom.xml | 2 +- security/providers/common/pom.xml | 2 +- security/providers/config-vault/pom.xml | 2 +- security/providers/google-login/pom.xml | 2 +- security/providers/header/pom.xml | 2 +- security/providers/http-auth/pom.xml | 2 +- security/providers/http-sign/pom.xml | 2 +- security/providers/idcs-mapper/pom.xml | 2 +- security/providers/jwt/pom.xml | 2 +- security/providers/oidc-common/pom.xml | 2 +- security/providers/oidc/pom.xml | 2 +- security/providers/pom.xml | 2 +- security/security/pom.xml | 2 +- security/util/pom.xml | 2 +- service/codegen/pom.xml | 2 +- service/pom.xml | 2 +- service/registry/pom.xml | 2 +- service/tests/codegen/pom.xml | 2 +- service/tests/pom.xml | 2 +- service/tests/registry/pom.xml | 2 +- tests/apps/bookstore/bookstore-mp/pom.xml | 2 +- tests/apps/bookstore/bookstore-se/pom.xml | 2 +- tests/apps/bookstore/common/pom.xml | 2 +- tests/apps/bookstore/pom.xml | 2 +- tests/apps/pom.xml | 2 +- tests/benchmark/jmh/pom.xml | 2 +- tests/benchmark/pom.xml | 2 +- tests/functional/bookstore/pom.xml | 2 +- tests/functional/config-profiles/pom.xml | 2 +- tests/functional/context-propagation/pom.xml | 2 +- tests/functional/jax-rs-multiple-apps/pom.xml | 2 +- tests/functional/jax-rs-subresource/pom.xml | 2 +- tests/functional/mp-compression/pom.xml | 2 +- tests/functional/mp-synthetic-app/pom.xml | 2 +- tests/functional/multiport/pom.xml | 2 +- .../param-converter-provider/pom.xml | 2 +- tests/functional/pom.xml | 2 +- tests/functional/request-scope-cdi/pom.xml | 2 +- .../request-scope-injection/pom.xml | 2 +- tests/functional/request-scope/pom.xml | 2 +- tests/integration/config/gh-2171-yml/pom.xml | 2 +- tests/integration/config/gh-2171/pom.xml | 2 +- tests/integration/config/gh-4375/pom.xml | 2 +- tests/integration/config/hocon-mp/pom.xml | 2 +- tests/integration/config/pom.xml | 2 +- tests/integration/dbclient/app/pom.xml | 4 +- tests/integration/dbclient/common/pom.xml | 2 +- tests/integration/dbclient/h2/pom.xml | 2 +- tests/integration/dbclient/pom.xml | 2 +- tests/integration/harness/pom.xml | 2 +- tests/integration/health/mp-disabled/pom.xml | 2 +- tests/integration/health/pom.xml | 2 +- tests/integration/jep290/check_f_f_ok/pom.xml | 2 +- tests/integration/jep290/check_f_f_w/pom.xml | 2 +- tests/integration/jep290/check_f_p_ok/pom.xml | 2 +- tests/integration/jep290/check_f_p_w/pom.xml | 2 +- tests/integration/jep290/pom.xml | 2 +- .../jep290/server_and_custom/pom.xml | 2 +- tests/integration/jep290/set_c_f_c/pom.xml | 2 +- tests/integration/jep290/set_c_f_d/pom.xml | 2 +- tests/integration/jep290/set_c_t_d/pom.xml | 2 +- tests/integration/jep290/set_f/pom.xml | 2 +- tests/integration/jep290/set_o/pom.xml | 2 +- tests/integration/jms/pom.xml | 2 +- tests/integration/jpa/appl/pom.xml | 2 +- tests/integration/jpa/model/pom.xml | 2 +- tests/integration/jpa/pom.xml | 2 +- tests/integration/jpa/simple/pom.xml | 2 +- tests/integration/kafka/pom.xml | 2 +- tests/integration/mp-bean-validation/pom.xml | 2 +- tests/integration/mp-gh-2421/pom.xml | 2 +- tests/integration/mp-gh-2461/pom.xml | 2 +- tests/integration/mp-gh-3246/pom.xml | 2 +- tests/integration/mp-gh-3974/pom.xml | 2 +- tests/integration/mp-gh-4123/pom.xml | 2 +- tests/integration/mp-gh-4654/pom.xml | 2 +- tests/integration/mp-gh-5328/pom.xml | 2 +- tests/integration/mp-gh-8349/pom.xml | 2 +- tests/integration/mp-gh-8478/pom.xml | 2 +- tests/integration/mp-gh-8493/pom.xml | 2 +- tests/integration/mp-gh-8495/pom.xml | 2 +- tests/integration/mp-graphql/pom.xml | 2 +- tests/integration/mp-security-client/pom.xml | 2 +- tests/integration/mp-ws-services/pom.xml | 2 +- tests/integration/native-image/mp-1/pom.xml | 2 +- tests/integration/native-image/mp-2/pom.xml | 2 +- tests/integration/native-image/mp-3/pom.xml | 2 +- tests/integration/native-image/pom.xml | 2 +- tests/integration/native-image/se-1/pom.xml | 2 +- .../native-image/static-content/pom.xml | 2 +- tests/integration/oidc/pom.xml | 2 +- tests/integration/pom.xml | 2 +- .../integration/restclient-connector/pom.xml | 2 +- tests/integration/restclient/pom.xml | 2 +- tests/integration/security/gh1487/pom.xml | 2 +- tests/integration/security/gh2297/pom.xml | 2 +- tests/integration/security/gh2455/pom.xml | 2 +- tests/integration/security/gh2772/pom.xml | 2 +- .../integration/security/path-params/pom.xml | 2 +- tests/integration/security/pom.xml | 2 +- .../security/security-annotation/pom.xml | 2 +- .../security/security-response-mapper/pom.xml | 2 +- .../integration/tls-revocation-config/pom.xml | 2 +- tests/integration/vault/pom.xml | 2 +- tests/integration/zipkin-mp-2.2/pom.xml | 2 +- tests/pom.xml | 2 +- tests/tck/pom.xml | 2 +- tests/tck/tck-reactive-streams/pom.xml | 2 +- tracing/config/pom.xml | 2 +- tracing/jersey-client/pom.xml | 2 +- tracing/jersey/pom.xml | 2 +- tracing/pom.xml | 2 +- tracing/provider-tests/pom.xml | 2 +- tracing/providers/jaeger/pom.xml | 2 +- tracing/providers/opentelemetry/pom.xml | 2 +- tracing/providers/opentracing/pom.xml | 2 +- tracing/providers/pom.xml | 2 +- tracing/providers/zipkin/pom.xml | 2 +- .../tests/it-tracing-client-zipkin/pom.xml | 2 +- tracing/tests/pom.xml | 2 +- tracing/tracer-resolver/pom.xml | 2 +- tracing/tracing/pom.xml | 2 +- webclient/api/pom.xml | 2 +- webclient/dns-resolver/first/pom.xml | 2 +- webclient/dns-resolver/pom.xml | 2 +- webclient/dns-resolver/round-robin/pom.xml | 2 +- webclient/http1/pom.xml | 2 +- webclient/http2/pom.xml | 2 +- webclient/metrics/pom.xml | 2 +- webclient/pom.xml | 2 +- webclient/security/pom.xml | 2 +- webclient/sse/pom.xml | 2 +- webclient/tests/http1/pom.xml | 2 +- webclient/tests/http2/pom.xml | 2 +- webclient/tests/pom.xml | 2 +- webclient/tests/webclient/pom.xml | 2 +- webclient/tracing/pom.xml | 2 +- webclient/webclient/pom.xml | 2 +- webclient/websocket/pom.xml | 2 +- webserver/access-log/pom.xml | 2 +- webserver/context/pom.xml | 2 +- webserver/cors/pom.xml | 2 +- webserver/graphql/pom.xml | 2 +- webserver/grpc/pom.xml | 2 +- webserver/http2/pom.xml | 2 +- webserver/observe/config/pom.xml | 2 +- webserver/observe/health/pom.xml | 2 +- webserver/observe/info/pom.xml | 2 +- webserver/observe/log/pom.xml | 2 +- webserver/observe/metrics/pom.xml | 2 +- webserver/observe/observe/pom.xml | 2 +- webserver/observe/pom.xml | 2 +- webserver/observe/tracing/pom.xml | 2 +- webserver/pom.xml | 2 +- webserver/security/pom.xml | 2 +- webserver/service-common/pom.xml | 2 +- webserver/sse/pom.xml | 2 +- webserver/static-content/pom.xml | 2 +- webserver/testing/junit5/http2/pom.xml | 2 +- webserver/testing/junit5/junit5/pom.xml | 2 +- webserver/testing/junit5/pom.xml | 2 +- webserver/testing/junit5/websocket/pom.xml | 2 +- webserver/testing/pom.xml | 2 +- webserver/tests/access-log/pom.xml | 2 +- webserver/tests/gh2631/pom.xml | 2 +- webserver/tests/grpc/pom.xml | 2 +- webserver/tests/http2/pom.xml | 2 +- webserver/tests/imperative/pom.xml | 2 +- webserver/tests/mtls/pom.xml | 2 +- webserver/tests/observe/health/pom.xml | 2 +- webserver/tests/observe/observe/pom.xml | 2 +- webserver/tests/observe/pom.xml | 2 +- webserver/tests/observe/security/pom.xml | 2 +- webserver/tests/observe/weighted/pom.xml | 2 +- webserver/tests/pom.xml | 2 +- webserver/tests/resource-limits/pom.xml | 2 +- webserver/tests/sse/pom.xml | 2 +- webserver/tests/static-content/pom.xml | 2 +- webserver/tests/upgrade/pom.xml | 2 +- webserver/tests/webserver/pom.xml | 2 +- webserver/tests/websocket/pom.xml | 2 +- webserver/webserver/pom.xml | 2 +- webserver/websocket/pom.xml | 2 +- websocket/pom.xml | 2 +- 704 files changed, 6560 insertions(+), 710 deletions(-) create mode 100644 all-config-meta.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ef7b209650..982e337990e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,51 @@ For Helidon 2.x releases please see [Helidon 2.x CHANGELOG.md](https://github.co For Helidon 1.x releases please see [Helidon 1.x CHANGELOG.md](https://github.com/oracle/helidon/blob/helidon-1.x/CHANGELOG.md) +## [4.0.9] + +This release contains important bugfixes and ehancements and is recommended for all users of Helidon 4. + +Java 21 is required to use Helidon 4.0.9. + +### CHANGES + +- Common: Parameters.first(String) generates java.lang.IndexOutOfB… [8723](https://github.com/helidon-io/helidon/pull/8723) +- Config: Cannot read config from environment with environment modules variables [8786](https://github.com/helidon-io/helidon/pull/8786) +- Config: LazyConfigSource is now queried when an unknown node is requested [8707](https://github.com/helidon-io/helidon/pull/8707) +- Config: Switched implementation of MpEnvironmentVariablesSource to use an LRU cache [8768](https://github.com/helidon-io/helidon/pull/8768) +- Config: fix `getOrdinal` for system property and environment variable config sources [8744](https://github.com/helidon-io/helidon/pull/8744) +- gRPC: Improvements to gRPC server-side support [8765](https://github.com/helidon-io/helidon/pull/8765) +- MP Threading: New annotation @ExecuteOn [8643](https://github.com/helidon-io/helidon/pull/8643) +- Native image: AbstractConfigurableExtension native-image fix [8771](https://github.com/helidon-io/helidon/pull/8771) +- Native image: Force hibernate to use no-op bytecode provider with native-image [8740](https://github.com/helidon-io/helidon/pull/8740) +- Native image: Register additional hibernate classes for reflection [8758](https://github.com/helidon-io/helidon/pull/8758) +- Native-image: Fix native-image properties layout. [8808](https://github.com/helidon-io/helidon/pull/8808) +- Native-image: Use native-image:compile-no-fork instead of native-image:compile [8802](https://github.com/helidon-io/helidon/pull/8802) +- OCI: Refactor OCI metrics library a bit [8745](https://github.com/helidon-io/helidon/pull/8745) +- Testing: @HelidonTest / @AddConfig* Provide a config parser by type #8718 [8721](https://github.com/helidon-io/helidon/pull/8721) +- Testing: Ability to Inject MockBeans in Helidon #7694 [8674](https://github.com/helidon-io/helidon/pull/8674) +- Testing: Add text block support [8655](https://github.com/helidon-io/helidon/pull/8655) +- Tracing: Associate tracer-level tags with Jaeger process level (instead of span level) [8764](https://github.com/helidon-io/helidon/pull/8764) +- Tracing: Fix problems with tracing data propagation [8742](https://github.com/helidon-io/helidon/pull/8742) +- Tracing: Harden WebClientSecurity against absent or disabled tracing [8809](https://github.com/helidon-io/helidon/pull/8809) +- Tracing: Use Helidon tracer, span builder, span types instead of OTel ones so we can trigger span listeners [8778](https://github.com/helidon-io/helidon/pull/8778) +- Build: Plugin updates [8687](https://github.com/helidon-io/helidon/pull/8687) +- Build: Update setup-java for snapshot workflow [8788](https://github.com/helidon-io/helidon/pull/8788) +- Build: cleanup helidon-bom and helidon-all [8783](https://github.com/helidon-io/helidon/pull/8783) +- Build: helidon-common sources JAR contains absolute paths #8761 [8762](https://github.com/helidon-io/helidon/pull/8762) +- Build: upgrade MacOS runner to 14 and fix protoc version [8717](https://github.com/helidon-io/helidon/pull/8717) +- Dependencies: Bump deploy plugin to 3.1.1 [8790](https://github.com/helidon-io/helidon/pull/8790) +- Dependencies: Upgrade microprofile rest client to 3.0.1 [8730](https://github.com/helidon-io/helidon/pull/8730) +- Dependencies: Upgrades to Jersey to 3.1.7 [8798](https://github.com/helidon-io/helidon/pull/8798) +- Docs: New document that describes the ExecuteOn annotation [8756](https://github.com/helidon-io/helidon/pull/8756) +- Docs: include SE upgrade guide in docs navbar [8795](https://github.com/helidon-io/helidon/pull/8795) +- Examples: Capitalized received message in io.helidon.examples.webserver.websocket.MessageBoardEndpoint (#8725) [8731](https://github.com/helidon-io/helidon/pull/8731) +- Examples: Update Dockerfiles to use jdk-no-fee-term instead of openjdk [8733](https://github.com/helidon-io/helidon/pull/8733) +- Examples: Use LevelChangePropagator in examples/logging/slf4j (#7737) [8656](https://github.com/helidon-io/helidon/pull/8656) +- Examples: Archetype - Add SLF4J dependency [8792](https://github.com/helidon-io/helidon/pull/8792) +- Tests: Add tests for building native-image for quickstart examples [8719](https://github.com/helidon-io/helidon/pull/8719) +- Tests: Fix tests/integration/native-image/mp-2 [8801](https://github.com/helidon-io/helidon/pull/8801) + ## [4.0.8] This release contains important bugfixes and ehancements and is recommended for all users of Helidon 4. @@ -1172,6 +1217,7 @@ Helidon 4.0.0 is a major release that includes significant new features and fixe - MicroProfile: MP path based static content should use index.html (4.x) [4737](https://github.com/oracle/helidon/pull/4737) - Build: 4.0 version and poms [4655](https://github.com/oracle/helidon/pull/4655) +[4.0.9]: https://github.com/oracle/helidon/compare/4.0.8...4.0.9 [4.0.8]: https://github.com/oracle/helidon/compare/4.0.7...4.0.8 [4.0.7]: https://github.com/oracle/helidon/compare/4.0.6...4.0.7 [4.0.6]: https://github.com/oracle/helidon/compare/4.0.5...4.0.6 diff --git a/all-config-meta.json b/all-config-meta.json new file mode 100644 index 00000000000..fe6f2421c93 --- /dev/null +++ b/all-config-meta.json @@ -0,0 +1,5804 @@ +[ + { + "module": "io.helidon.webclient.http1", + "types": [ + { + "annotatedType": "io.helidon.webclient.http1.Http1ClientProtocolConfig", + "type": "io.helidon.webclient.http1.Http1ClientProtocolConfig", + "producers": [ + "io.helidon.webclient.http1.Http1ClientProtocolConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.http1.Http1ClientProtocolConfig#builder()" + ], + "options": [ + { + "defaultValue": "true", + "description": "Sets whether the response header format is validated or not.\n

    \n Defaults to `true`.\n

    \n\n @return whether response header validation should be enabled", + "key": "validate-response-headers", + "method": "io.helidon.webclient.http1.Http1ClientProtocolConfig.Builder#validateResponseHeaders(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "256", + "description": "Configure the maximum allowed length of the status line from the response.\n\n @return maximum status line length", + "key": "max-status-line-length", + "method": "io.helidon.webclient.http1.Http1ClientProtocolConfig.Builder#maxStatusLineLength(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "false", + "description": "Sets whether the request header format is validated or not.\n

    \n Defaults to `false` as user has control on the header creation.\n

    \n\n @return whether request header validation should be enabled", + "key": "validate-request-headers", + "method": "io.helidon.webclient.http1.Http1ClientProtocolConfig.Builder#validateRequestHeaders(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "http_1_1", + "description": "", + "key": "name", + "method": "io.helidon.webclient.http1.Http1ClientProtocolConfig.Builder#name(java.lang.String)" + }, + { + "defaultValue": "16384", + "description": "Configure the maximum allowed header size of the response.\n\n @return maximum header size", + "key": "max-header-size", + "method": "io.helidon.webclient.http1.Http1ClientProtocolConfig.Builder#maxHeaderSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Whether to use keep alive by default.\n\n @return `true` for keeping connections alive and re-using them for multiple requests (default), `false`\n to create a new connection for each request", + "key": "default-keep-alive", + "method": "io.helidon.webclient.http1.Http1ClientProtocolConfig.Builder#defaultKeepAlive(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webclient.websocket", + "types": [ + { + "annotatedType": "io.helidon.webclient.websocket.WsClientProtocolConfig", + "type": "io.helidon.webclient.websocket.WsClientProtocolConfig", + "producers": [ + "io.helidon.webclient.websocket.WsClientProtocolConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.websocket.WsClientProtocolConfig#builder()" + ], + "options": [ + { + "description": "", + "key": "sub-protocols", + "kind": "LIST", + "method": "io.helidon.webclient.websocket.WsClientProtocolConfig.Builder#subProtocols(java.util.List)" + }, + { + "defaultValue": "websocket", + "description": "", + "key": "name", + "method": "io.helidon.webclient.websocket.WsClientProtocolConfig.Builder#name(java.lang.String)" + } + ] + }, + { + "annotatedType": "io.helidon.webclient.websocket.WsClientConfig", + "type": "io.helidon.webclient.websocket.WsClient", + "producers": [ + "io.helidon.webclient.websocket.WsClientConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.websocket.WsClientConfig#builder()", + "io.helidon.webclient.websocket.WsClient#create(io.helidon.webclient.websocket.WsClientConfig)" + ], + "options": [ + { + "defaultValue": "create()", + "description": "WebSocket specific configuration.\n\n @return protocol specific configuration", + "key": "protocol-config", + "method": "io.helidon.webclient.websocket.WsClientConfig.Builder#protocolConfig(io.helidon.webclient.websocket.WsClientProtocolConfig)", + "type": "io.helidon.webclient.websocket.WsClientProtocolConfig" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webclient.http2", + "types": [ + { + "annotatedType": "io.helidon.webclient.http2.Http2ClientProtocolConfig", + "type": "io.helidon.webclient.http2.Http2ClientProtocolConfig", + "producers": [ + "io.helidon.webclient.http2.Http2ClientProtocolConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.http2.Http2ClientProtocolConfig#builder()" + ], + "options": [ + { + "defaultValue": "false", + "description": "Prior knowledge of HTTP/2 capabilities of the server. If server we are connecting to does not\n support HTTP/2 and prior knowledge is set to `false`, only features supported by HTTP/1 will be available\n and attempts to use HTTP/2 specific will throw an UnsupportedOperationException.\n

    Plain text connection

    \n If prior knowledge is set to `true`, we will not attempt an upgrade of connection and use prior knowledge.\n If prior knowledge is set to `false`, we will initiate an HTTP/1 connection and upgrade it to HTTP/2,\n if supported by the server.\n plaintext connection (`h2c`).\n

    TLS protected connection

    \n If prior knowledge is set to `true`, we will negotiate protocol using HTTP/2 only, failing if not supported.\n if prior knowledge is set to `false`, we will negotiate protocol using both HTTP/2 and HTTP/1, using the protocol\n supported by server.\n\n @return whether to use prior knowledge of HTTP/2", + "key": "prior-knowledge", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#priorKnowledge(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "PT0.1S", + "description": "Timeout for blocking between windows size check iterations.\n\n @return timeout", + "key": "flow-control-block-timeout", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#flowControlBlockTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "PT0.5S", + "description": "Timeout for ping probe used for checking healthiness of cached connections.\n Defaults to `PT0.5S`, which means 500 milliseconds.\n\n @return timeout", + "key": "ping-timeout", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#pingTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "16384", + "description": "Configure initial MAX_FRAME_SIZE setting for new HTTP/2 connections.\n Maximum size of data frames in bytes the client is prepared to accept from the server.\n Default value is 2^14(16_384).\n\n @return data frame size in bytes between 2^14(16_384) and 2^24-1(16_777_215)", + "key": "max-frame-size", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#maxFrameSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "false", + "description": "Check healthiness of cached connections with HTTP/2.0 ping frame.\n Defaults to `false`.\n\n @return use ping if true", + "key": "ping", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#ping(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "-1", + "description": "Configure initial MAX_HEADER_LIST_SIZE setting for new HTTP/2 connections.\n Sends to the server the maximum header field section size client is prepared to accept.\n Defaults to `-1`, which means \"unconfigured\".\n\n @return units of octets", + "key": "max-header-list-size", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#maxHeaderListSize(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "h2", + "description": "", + "key": "name", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#name(java.lang.String)" + }, + { + "defaultValue": "65535", + "description": "Configure INITIAL_WINDOW_SIZE setting for new HTTP/2 connections.\n Sends to the server the size of the largest frame payload client is willing to receive.\n Defaults to {@value io.helidon.http.http2.WindowSize#DEFAULT_WIN_SIZE}.\n\n @return units of octets", + "key": "initial-window-size", + "method": "io.helidon.webclient.http2.Http2ClientProtocolConfig.Builder#initialWindowSize(int)", + "type": "java.lang.Integer" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webclient.api", + "types": [ + { + "annotatedType": "io.helidon.webclient.api.WebClientConfig", + "prefix": "clients", + "type": "io.helidon.webclient.api.WebClient", + "standalone": true, + "inherits": [ + "io.helidon.webclient.api.HttpClientConfig" + ], + "producers": [ + "io.helidon.webclient.api.WebClientConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.WebClientConfig#builder()", + "io.helidon.webclient.api.WebClient#create(io.helidon.webclient.api.WebClientConfig)" + ], + "options": [ + { + "description": "Configuration of client protocols.\n\n @return client protocol configurations", + "key": "protocol-configs", + "kind": "LIST", + "method": "io.helidon.webclient.api.WebClientConfig.Builder#protocolConfigs(java.util.List)", + "providerType": "io.helidon.webclient.spi.ProtocolConfigProvider", + "type": "io.helidon.webclient.spi.ProtocolConfig", + "provider": true + } + ] + }, + { + "annotatedType": "io.helidon.webclient.api.HttpClientConfig", + "type": "io.helidon.webclient.api.HttpClientConfig", + "inherits": [ + "io.helidon.webclient.api.HttpConfigBase" + ], + "producers": [ + "io.helidon.webclient.api.HttpClientConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.HttpClientConfig#builder()" + ], + "options": [ + { + "defaultValue": "false", + "description": "Can be set to `true` to force the use of relative URIs in all requests,\n regardless of the presence or absence of proxies or no-proxy lists.\n\n @return relative URIs flag", + "key": "relative-uris", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#relativeUris(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Default headers to be used in every request from configuration.\n\n @return default headers", + "key": "default-headers", + "kind": "MAP", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#defaultHeadersMap(java.util.Map)" + }, + { + "description": "Configure the listener specific io.helidon.http.encoding.ContentEncodingContext.\n This method discards all previously registered ContentEncodingContext.\n If no content encoding context is registered, default encoding context is used.\n\n @return content encoding context", + "key": "content-encoding", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#contentEncoding(io.helidon.http.encoding.ContentEncodingContext)", + "type": "io.helidon.http.encoding.ContentEncodingContext" + }, + { + "defaultValue": "256", + "description": "Maximal size of the connection cache.\n For most HTTP protocols, we may cache connections to various endpoints for keep alive (or stream reuse in case of HTTP/2).\n This option limits the size. Setting this number lower than the \"usual\" number of target services will cause connections\n to be closed and reopened frequently.", + "key": "connection-cache-size", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#connectionCacheSize(int)", + "type": "java.lang.Integer" + }, + { + "description": "WebClient services.\n\n @return services to use with this web client", + "key": "services", + "kind": "LIST", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#services(java.util.List)", + "providerType": "io.helidon.webclient.spi.WebClientServiceProvider", + "type": "io.helidon.webclient.spi.WebClientService", + "provider": true + }, + { + "defaultValue": "create()", + "description": "Configure the listener specific io.helidon.http.media.MediaContext.\n This method discards all previously registered MediaContext.\n If no media context is registered, default media context is used.\n\n @return media context", + "key": "media-context", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#mediaContext(io.helidon.http.media.MediaContext)", + "type": "io.helidon.http.media.MediaContext" + }, + { + "description": "WebClient cookie manager.\n\n @return cookie manager to use", + "key": "cookie-manager", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#cookieManager(java.util.Optional)", + "type": "io.helidon.webclient.api.WebClientCookieManager" + }, + { + "defaultValue": "131072", + "description": "If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance.\n If bigger, streaming will be used.\n

    \n Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such\n cases, this option is ignored. Default is 128Kb.\n\n @return maximal number of bytes to buffer in memory for supported writers", + "key": "max-in-memory-entity", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#maxInMemoryEntity(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Whether Expect-100-Continue header is sent to verify server availability before sending an entity.\n

    \n Defaults to `true`.\n

    \n\n @return whether Expect:100-Continue header should be sent on streamed transfers", + "key": "send-expect-continue", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#sendExpectContinue(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Socket options for connections opened by this client.\n If there is a value explicitly configured on this type and on the socket options,\n the one configured on this type's builder will win:\n
      \n
    • #readTimeout()
    • \n
    • #connectTimeout()
    • \n
    \n\n @return socket options", + "key": "socket-options", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#socketOptions(io.helidon.common.socket.SocketOptions)", + "type": "io.helidon.common.socket.SocketOptions" + }, + { + "defaultValue": "true", + "description": "Whether to share connection cache between all the WebClient instances in JVM.\n\n @return true if connection cache is shared", + "key": "share-connection-cache", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#shareConnectionCache(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "STRICT", + "description": "Configure media type parsing mode for HTTP `Content-Type` header.\n\n @return media type parsing mode", + "key": "media-type-parser-mode", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#mediaTypeParserMode(io.helidon.common.media.type.ParserMode)", + "type": "io.helidon.common.media.type.ParserMode", + "allowedValues": [ + { + "description": "", + "value": "STRICT" + }, + { + "description": "", + "value": "RELAXED" + } + ] + }, + { + "description": "Base uri used by the client in all requests.\n\n @return base uri of the client requests", + "key": "base-uri", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#baseUri(java.util.Optional)", + "type": "io.helidon.webclient.api.ClientUri" + }, + { + "defaultValue": "PT1S", + "description": "Socket 100-Continue read timeout. Default is 1 second.\n This read timeout is used when 100-Continue is sent by the client, before it sends an entity.\n\n @return read 100-Continue timeout duration", + "key": "read-continue-timeout", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#readContinueTimeout(java.time.Duration)", + "type": "java.time.Duration" + } + ] + }, + { + "annotatedType": "io.helidon.webclient.api.WebClientCookieManagerConfig", + "type": "io.helidon.webclient.api.WebClientCookieManager", + "producers": [ + "io.helidon.webclient.api.WebClientCookieManagerConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.WebClientCookieManagerConfig#builder()", + "io.helidon.webclient.api.WebClientCookieManager#create(io.helidon.webclient.api.WebClientCookieManagerConfig)" + ], + "options": [ + { + "defaultValue": "false", + "description": "Whether automatic cookie store is enabled or not.\n\n @return status of cookie store", + "key": "automatic-store-enabled", + "method": "io.helidon.webclient.api.WebClientCookieManagerConfig.Builder#automaticStoreEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER", + "description": "Current cookie policy for this client.\n\n @return the cookie policy", + "key": "cookie-policy", + "method": "io.helidon.webclient.api.WebClientCookieManagerConfig.Builder#cookiePolicy(java.net.CookiePolicy)", + "type": "java.net.CookiePolicy" + }, + { + "description": "Map of default cookies to include in all requests if cookies enabled.\n\n @return map of default cookies", + "key": "default-cookies", + "kind": "MAP", + "method": "io.helidon.webclient.api.WebClientCookieManagerConfig.Builder#defaultCookies(java.util.Map)" + } + ] + }, + { + "annotatedType": "io.helidon.webclient.api.HttpConfigBase", + "type": "io.helidon.webclient.api.HttpConfigBase", + "producers": [ + "io.helidon.webclient.api.HttpConfigBase#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.HttpConfigBase#builder()" + ], + "options": [ + { + "description": "Read timeout.\n\n @return read timeout\n @see io.helidon.common.socket.SocketOptions#readTimeout()", + "key": "read-timeout", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#readTimeout(java.util.Optional)", + "type": "java.time.Duration" + }, + { + "defaultValue": "true", + "description": "Determines if connection keep alive is enabled (NOT socket keep alive, but HTTP connection keep alive, to re-use\n the same connection for multiple requests).\n\n @return keep alive for this connection\n @see io.helidon.common.socket.SocketOptions#socketKeepAlive()", + "key": "keep-alive", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#keepAlive(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Proxy configuration to be used for requests.\n\n @return proxy to use, defaults to Proxy#noProxy()", + "key": "proxy", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#proxy(io.helidon.webclient.api.Proxy)", + "type": "io.helidon.webclient.api.Proxy" + }, + { + "defaultValue": "true", + "description": "Whether to follow redirects.\n\n @return whether to follow redirects", + "key": "follow-redirects", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#followRedirects(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Connect timeout.\n\n @return connect timeout\n @see io.helidon.common.socket.SocketOptions#connectTimeout()", + "key": "connect-timeout", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#connectTimeout(java.util.Optional)", + "type": "java.time.Duration" + }, + { + "defaultValue": "10", + "description": "Max number of followed redirects.\n This is ignored if #followRedirects() option is `false`.\n\n @return max number of followed redirects", + "key": "max-redirects", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#maxRedirects(int)", + "type": "java.lang.Integer" + }, + { + "description": "TLS configuration for any TLS request from this client.\n TLS can also be configured per request.\n TLS is used when the protocol is set to `https`.\n\n @return TLS configuration to use", + "key": "tls", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#tls(io.helidon.common.tls.Tls)", + "type": "io.helidon.common.tls.Tls" + }, + { + "description": "Properties configured for this client. These properties are propagated through client request, to be used by\n services (and possibly for other purposes).\n\n @return map of client properties", + "key": "properties", + "kind": "MAP", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#properties(java.util.Map)" + } + ] + }, + { + "annotatedType": "io.helidon.webclient.api.Proxy.Builder", + "type": "io.helidon.webclient.api.Proxy", + "producers": [ + "io.helidon.webclient.api.Proxy.Builder#build()", + "io.helidon.webclient.api.Proxy#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Sets a new password for the proxy.", + "key": "password", + "method": "io.helidon.webclient.api.Proxy.Builder#password(char[])" + }, + { + "description": "Sets a port value.", + "key": "port", + "method": "io.helidon.webclient.api.Proxy.Builder#port(int)", + "type": "java.lang.Integer" + }, + { + "description": "Sets a new host value.", + "key": "host", + "method": "io.helidon.webclient.api.Proxy.Builder#host(java.lang.String)" + }, + { + "description": "Sets a new username for the proxy.", + "key": "username", + "method": "io.helidon.webclient.api.Proxy.Builder#username(java.lang.String)" + }, + { + "defaultValue": "HTTP", + "description": "Sets a new proxy type.", + "key": "type", + "method": "io.helidon.webclient.api.Proxy.Builder#type(io.helidon.webclient.api.Proxy.ProxyType)", + "type": "io.helidon.webclient.api.Proxy.ProxyType", + "allowedValues": [ + { + "description": "No proxy.", + "value": "NONE" + }, + { + "description": "Proxy obtained from system.", + "value": "SYSTEM" + }, + { + "description": "HTTP proxy.", + "value": "HTTP" + } + ] + }, + { + "description": "Configure a host pattern that is not going through a proxy.\n

    \n Options are:\n

      \n
    • IP Address, such as `192.168.1.1`
    • \n
    • IP V6 Address, such as `[2001:db8:85a3:8d3:1319:8a2e:370:7348]`
    • \n
    • Hostname, such as `localhost`
    • \n
    • Domain name, such as `helidon.io`
    • \n
    • Domain name and all sub-domains, such as `.helidon.io` (leading dot)
    • \n
    • Combination of all options from above with a port, such as `.helidon.io:80`
    • \n
    ", + "key": "no-proxy", + "kind": "LIST", + "method": "io.helidon.webclient.api.Proxy.Builder#addNoProxy(java.lang.String)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.metrics.api", + "types": [ + { + "annotatedType": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig", + "type": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig", + "producers": [ + "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig#builder()" + ], + "options": [ + { + "defaultValue": "PT10S", + "description": "Threshold in ms that characterizes whether a request is long running.\n\n @return threshold in ms indicating a long-running request", + "key": "long-running-requests.threshold", + "method": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig.Builder#longRunningRequestThreshold(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "false", + "description": "Whether KPI extended metrics are enabled.\n\n @return true if KPI extended metrics are enabled; false otherwise", + "key": "extended", + "method": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig.Builder#extended(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.ScopingConfig", + "type": "io.helidon.metrics.api.ScopingConfig", + "producers": [ + "io.helidon.metrics.api.ScopingConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.ScopingConfig#builder()" + ], + "options": [ + { + "defaultValue": "application", + "description": "Default scope value to associate with meters that are registered without an explicit setting; no setting means meters\n are assigned scope {@value io.helidon.metrics.api.Meter.Scope#DEFAULT}.\n\n @return default scope value", + "key": "default", + "method": "io.helidon.metrics.api.ScopingConfig.Builder#defaultValue(java.util.Optional)" + }, + { + "defaultValue": "scope", + "description": "Tag name for storing meter scope values in the underlying implementation meter registry.\n\n @return tag name for storing scope values", + "key": "tag-name", + "method": "io.helidon.metrics.api.ScopingConfig.Builder#tagName(java.util.Optional)" + }, + { + "description": "Settings for individual scopes.\n\n @return scope settings", + "key": "scopes", + "kind": "MAP", + "method": "io.helidon.metrics.api.ScopingConfig.Builder#scopes(java.util.Map)", + "type": "io.helidon.metrics.api.ScopeConfig" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.MetricsConfig", + "prefix": "metrics", + "type": "io.helidon.metrics.api.MetricsConfig", + "standalone": true, + "producers": [ + "io.helidon.metrics.api.MetricsConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.MetricsConfig#builder()" + ], + "options": [ + { + "description": "Whether automatic REST request metrics should be measured.\n\n @return true/false", + "key": "rest-request-enabled", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#restRequestEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Name for the application tag to be added to each meter ID.\n\n @return application tag name", + "key": "app-tag-name", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#appTagName(java.util.Optional)" + }, + { + "defaultValue": "observe", + "description": "Hints for role names the user is expected to be in.\n\n @return list of hints", + "key": "roles", + "kind": "LIST", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#roles(java.util.List)" + }, + { + "description": "Key performance indicator metrics settings.\n\n @return key performance indicator metrics settings", + "key": "key-performance-indicators", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#keyPerformanceIndicatorMetricsConfig(io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig)", + "type": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig" + }, + { + "defaultValue": "true", + "description": "Whether to allow anybody to access the endpoint.\n\n @return whether to permit access to metrics endpoint to anybody, defaults to `true`\n @see #roles()", + "key": "permit-all", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#permitAll(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Settings related to scoping management.\n\n @return scoping settings", + "key": "scoping", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#scoping(io.helidon.metrics.api.ScopingConfig)", + "type": "io.helidon.metrics.api.ScopingConfig" + }, + { + "description": "Global tags.\n\n @return name/value pairs for global tags", + "key": "tags", + "kind": "LIST", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#tags(java.util.List)", + "type": "io.helidon.metrics.api.Tag" + }, + { + "description": "Value for the application tag to be added to each meter ID.\n\n @return application tag value", + "key": "app-name", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#appName(java.util.Optional)" + }, + { + "defaultValue": "true", + "description": "Whether metrics functionality is enabled.\n\n @return if metrics are configured to be enabled", + "key": "enabled", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.ScopeConfig", + "type": "io.helidon.metrics.api.ScopeConfig", + "producers": [ + "io.helidon.metrics.api.ScopeConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.ScopeConfig#builder()" + ], + "options": [ + { + "description": "Regular expression for meter names to include.\n\n @return include expression", + "key": "filter.include", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#include(java.util.Optional)", + "type": "java.util.regex.Pattern" + }, + { + "description": "Name of the scope to which the configuration applies.\n\n @return scope name", + "key": "name", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#name(java.lang.String)" + }, + { + "description": "Regular expression for meter names to exclude.\n\n @return exclude expression", + "key": "filter.exclude", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#exclude(java.util.Optional)", + "type": "java.util.regex.Pattern" + }, + { + "defaultValue": "true", + "description": "Whether the scope is enabled.\n\n @return if the scope is enabled", + "key": "enabled", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.ComponentMetricsSettings.Builder", + "prefix": "metrics", + "type": "io.helidon.metrics.api.ComponentMetricsSettings.Builder", + "options": [ + { + "description": "Sets whether metrics should be enabled for the component.", + "key": "enabled", + "method": "io.helidon.metrics.api.ComponentMetricsSettings.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.Tag", + "type": "io.helidon.metrics.api.Tag", + "options": [] + } + ] + } +] +[ + { + "module": "io.helidon.metrics.api", + "types": [ + { + "annotatedType": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig", + "type": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig", + "producers": [ + "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig#builder()" + ], + "options": [ + { + "defaultValue": "PT10S", + "description": "Threshold in ms that characterizes whether a request is long running.\n\n @return threshold in ms indicating a long-running request", + "key": "long-running-requests.threshold", + "method": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig.Builder#longRunningRequestThreshold(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "false", + "description": "Whether KPI extended metrics are enabled.\n\n @return true if KPI extended metrics are enabled; false otherwise", + "key": "extended", + "method": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig.Builder#extended(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.ScopingConfig", + "type": "io.helidon.metrics.api.ScopingConfig", + "producers": [ + "io.helidon.metrics.api.ScopingConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.ScopingConfig#builder()" + ], + "options": [ + { + "defaultValue": "application", + "description": "Default scope value to associate with meters that are registered without an explicit setting; no setting means meters\n are assigned scope {@value io.helidon.metrics.api.Meter.Scope#DEFAULT}.\n\n @return default scope value", + "key": "default", + "method": "io.helidon.metrics.api.ScopingConfig.Builder#defaultValue(java.util.Optional)" + }, + { + "defaultValue": "scope", + "description": "Tag name for storing meter scope values in the underlying implementation meter registry.\n\n @return tag name for storing scope values", + "key": "tag-name", + "method": "io.helidon.metrics.api.ScopingConfig.Builder#tagName(java.util.Optional)" + }, + { + "description": "Settings for individual scopes.\n\n @return scope settings", + "key": "scopes", + "kind": "MAP", + "method": "io.helidon.metrics.api.ScopingConfig.Builder#scopes(java.util.Map)", + "type": "io.helidon.metrics.api.ScopeConfig" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.MetricsConfig", + "prefix": "metrics", + "type": "io.helidon.metrics.api.MetricsConfig", + "standalone": true, + "producers": [ + "io.helidon.metrics.api.MetricsConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.MetricsConfig#builder()" + ], + "options": [ + { + "description": "Whether automatic REST request metrics should be measured.\n\n @return true/false", + "key": "rest-request-enabled", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#restRequestEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Name for the application tag to be added to each meter ID.\n\n @return application tag name", + "key": "app-tag-name", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#appTagName(java.util.Optional)" + }, + { + "defaultValue": "observe", + "description": "Hints for role names the user is expected to be in.\n\n @return list of hints", + "key": "roles", + "kind": "LIST", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#roles(java.util.List)" + }, + { + "description": "Key performance indicator metrics settings.\n\n @return key performance indicator metrics settings", + "key": "key-performance-indicators", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#keyPerformanceIndicatorMetricsConfig(io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig)", + "type": "io.helidon.metrics.api.KeyPerformanceIndicatorMetricsConfig" + }, + { + "defaultValue": "true", + "description": "Whether to allow anybody to access the endpoint.\n\n @return whether to permit access to metrics endpoint to anybody, defaults to `true`\n @see #roles()", + "key": "permit-all", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#permitAll(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Settings related to scoping management.\n\n @return scoping settings", + "key": "scoping", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#scoping(io.helidon.metrics.api.ScopingConfig)", + "type": "io.helidon.metrics.api.ScopingConfig" + }, + { + "description": "Global tags.\n\n @return name/value pairs for global tags", + "key": "tags", + "kind": "LIST", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#tags(java.util.List)", + "type": "io.helidon.metrics.api.Tag" + }, + { + "description": "Value for the application tag to be added to each meter ID.\n\n @return application tag value", + "key": "app-name", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#appName(java.util.Optional)" + }, + { + "defaultValue": "true", + "description": "Whether metrics functionality is enabled.\n\n @return if metrics are configured to be enabled", + "key": "enabled", + "method": "io.helidon.metrics.api.MetricsConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.ScopeConfig", + "type": "io.helidon.metrics.api.ScopeConfig", + "producers": [ + "io.helidon.metrics.api.ScopeConfig#create(io.helidon.common.config.Config)", + "io.helidon.metrics.api.ScopeConfig#builder()" + ], + "options": [ + { + "description": "Regular expression for meter names to include.\n\n @return include expression", + "key": "filter.include", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#include(java.util.Optional)", + "type": "java.util.regex.Pattern" + }, + { + "description": "Name of the scope to which the configuration applies.\n\n @return scope name", + "key": "name", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#name(java.lang.String)" + }, + { + "description": "Regular expression for meter names to exclude.\n\n @return exclude expression", + "key": "filter.exclude", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#exclude(java.util.Optional)", + "type": "java.util.regex.Pattern" + }, + { + "defaultValue": "true", + "description": "Whether the scope is enabled.\n\n @return if the scope is enabled", + "key": "enabled", + "method": "io.helidon.metrics.api.ScopeConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.ComponentMetricsSettings.Builder", + "prefix": "metrics", + "type": "io.helidon.metrics.api.ComponentMetricsSettings.Builder", + "options": [ + { + "description": "Sets whether metrics should be enabled for the component.", + "key": "enabled", + "method": "io.helidon.metrics.api.ComponentMetricsSettings.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.metrics.api.Tag", + "type": "io.helidon.metrics.api.Tag", + "options": [] + } + ] + } +] +[ + { + "module": "io.helidon.tracing", + "types": [ + { + "annotatedType": "io.helidon.tracing.TracerBuilder", + "description": "Tracer configuration.", + "type": "io.helidon.tracing.TracerBuilder", + "producers": [ + "io.helidon.tracing.TracerBuilder#create(io.helidon.common.config.Config)" + ], + "options": [] + } + ] + } +] +[ + { + "module": "io.helidon.tracing.providers.opentracing", + "types": [ + { + "annotatedType": "io.helidon.tracing.providers.opentracing.OpenTracingTracerBuilder", + "description": "OpenTracing tracer configuration.", + "type": "io.helidon.tracing.providers.opentracing.OpenTracingTracerBuilder", + "producers": [ + "io.helidon.tracing.providers.opentracing.OpenTracingTracerBuilder#create(io.helidon.common.config.Config)" + ], + "options": [] + } + ] + } +] +[ + { + "module": "io.helidon.tracing", + "types": [ + { + "annotatedType": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder", + "description": "Jaeger tracer configuration.", + "prefix": "tracing", + "type": "io.helidon.tracing.Tracer", + "standalone": true, + "inherits": [ + "io.helidon.tracing.TracerBuilder" + ], + "producers": [ + "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#build()" + ], + "options": [ + { + "defaultValue": "512", + "description": "Maximum Export Batch Size of exporter requests.", + "key": "max-export-batch-size", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#maxExportBatchSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "PT5S", + "description": "Schedule Delay of exporter requests.", + "key": "schedule-delay", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#scheduleDelay(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "JAEGER", + "description": "Add propagation format to use.", + "key": "propagation", + "kind": "LIST", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#addPropagation(io.helidon.tracing.providers.jaeger.JaegerTracerBuilder.PropagationFormat)", + "type": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder.PropagationFormat", + "allowedValues": [ + { + "description": "The Zipkin B3 trace context propagation format using multiple headers.", + "value": "B3" + }, + { + "description": "B3 trace context propagation using a single header.", + "value": "B3_SINGLE" + }, + { + "description": "The Jaeger trace context propagation format.", + "value": "JAEGER" + }, + { + "description": "The W3C trace context propagation format.", + "value": "W3C" + } + ] + }, + { + "description": "Private key in PEM format.", + "key": "private-key-pem", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#privateKey(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "defaultValue": "PT10S", + "description": "Timeout of exporter requests.", + "key": "exporter-timeout", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#exporterTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "CONSTANT", + "description": "Sampler type.\n

    \n See Sampler types.", + "key": "sampler-type", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#samplerType(io.helidon.tracing.providers.jaeger.JaegerTracerBuilder.SamplerType)", + "type": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder.SamplerType", + "allowedValues": [ + { + "description": "Constant sampler always makes the same decision for all traces.\n It either samples all traces `1` or none of them `0`.", + "value": "CONSTANT" + }, + { + "description": "Ratio of the requests to sample, double value.", + "value": "RATIO" + } + ] + }, + { + "description": "Trusted certificates in PEM format.", + "key": "trusted-cert-pem", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#trustedCertificates(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "defaultValue": "batch", + "description": "Span Processor type used.", + "key": "span-processor-type", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#spanProcessorType(io.helidon.tracing.providers.jaeger.JaegerTracerBuilder.SpanProcessorType)", + "type": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder.SpanProcessorType", + "allowedValues": [ + { + "description": "Simple Span Processor.", + "value": "SIMPLE" + }, + { + "description": "Batch Span Processor.", + "value": "BATCH" + } + ] + }, + { + "defaultValue": "1", + "description": "The sampler parameter (number).", + "key": "sampler-param", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#samplerParam(java.lang.Number)", + "type": "java.lang.Number" + }, + { + "description": "Certificate of client in PEM format.", + "key": "client-cert-pem", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#clientCertificate(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "defaultValue": "2048", + "description": "Maximum Queue Size of exporter requests.", + "key": "max-queue-size", + "method": "io.helidon.tracing.providers.jaeger.JaegerTracerBuilder#maxQueueSize(int)", + "type": "java.lang.Integer" + } + ] + } + ] + } +] +[ + { + "module": "io.opentracing.api", + "types": [ + { + "annotatedType": "io.helidon.tracing.providers.zipkin.ZipkinTracerBuilder", + "description": "Zipkin tracer configuration", + "prefix": "tracing", + "type": "io.opentracing.Tracer", + "standalone": true, + "inherits": [ + "io.helidon.tracing.providers.opentracing.OpenTracingTracerBuilder" + ], + "producers": [ + "io.helidon.tracing.providers.zipkin.ZipkinTracerBuilder#build()" + ], + "options": [ + { + "defaultValue": "V2", + "description": "Version of Zipkin API to use.\n Defaults to Version#V2.", + "key": "api-version", + "method": "io.helidon.tracing.providers.zipkin.ZipkinTracerBuilder#version(io.helidon.tracing.providers.zipkin.ZipkinTracerBuilder.Version)", + "type": "io.helidon.tracing.providers.zipkin.ZipkinTracerBuilder.Version", + "allowedValues": [ + { + "description": "Version 1.", + "value": "V1" + }, + { + "description": "Version 2.", + "value": "V2" + } + ] + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.dbclient.jdbc", + "types": [ + { + "annotatedType": "io.helidon.dbclient.jdbc.JdbcParametersConfig", + "prefix": "parameters", + "type": "io.helidon.dbclient.jdbc.JdbcParametersConfig", + "producers": [ + "io.helidon.dbclient.jdbc.JdbcParametersConfig#create(io.helidon.common.config.Config)", + "io.helidon.dbclient.jdbc.JdbcParametersConfig#builder()" + ], + "options": [ + { + "defaultValue": "true", + "description": "Use java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream, int) binding\n for `byte[]` values.\n Default value is `true`.\n\n @return whether to use java.io.ByteArrayInputStream binding", + "key": "use-byte-array-binding", + "method": "io.helidon.dbclient.jdbc.JdbcParametersConfig.Builder#useByteArrayBinding(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Use java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, int) binding\n for String values with length above #stringBindingSize() limit.\n Default value is `true`.\n\n @return whether to use java.io.CharArrayReader binding", + "key": "use-string-binding", + "method": "io.helidon.dbclient.jdbc.JdbcParametersConfig.Builder#useStringBinding(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Use java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp)\n to set java.time.LocalTime values when `true`\n or use java.sql.PreparedStatement#setTime(int, java.sql.Time) when `false`.\n Default value is `true`.\n

    This option is vendor specific. Most of the databases are fine with java.sql.Timestamp,\n but for example SQL Server requires java.sql.Time.\n This option does not apply when #setObjectForJavaTime() is set to `true`.\n\n @return whether to use java.sql.Timestamp instead of java.sql.Time\n for java.time.LocalTime values", + "key": "timestamp-for-local-time", + "method": "io.helidon.dbclient.jdbc.JdbcParametersConfig.Builder#timestampForLocalTime(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Use SQL `NCHAR`, `NVARCHAR` or `LONGNVARCHAR` value conversion\n for String values.\n Default value is `false`.\n\n @return whether NString conversion is used", + "key": "use-n-string", + "method": "io.helidon.dbclient.jdbc.JdbcParametersConfig.Builder#useNString(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "1024", + "description": "String values with length above this limit will be bound\n using java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, int)\n if #useStringBinding() is set to `true`.\n Default value is `1024`.\n\n @return String values length limit for java.io.CharArrayReader binding", + "key": "string-binding-size", + "method": "io.helidon.dbclient.jdbc.JdbcParametersConfig.Builder#stringBindingSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Set all `java.time` Date/Time values directly using java.sql.PreparedStatement#setObject(int, Object).\n This option shall work fine for recent JDBC drivers.\n Default value is `true`.\n\n @return whether to use java.sql.PreparedStatement#setObject(int, Object) for `java.time` Date/Time values", + "key": "set-object-for-java-time", + "method": "io.helidon.dbclient.jdbc.JdbcParametersConfig.Builder#setObjectForJavaTime(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.scheduling", + "types": [ + { + "annotatedType": "io.helidon.scheduling.TaskConfig", + "type": "io.helidon.scheduling.TaskConfig", + "producers": [ + "io.helidon.scheduling.TaskConfig#create(io.helidon.common.config.Config)", + "io.helidon.scheduling.TaskConfig#builder()" + ], + "options": [] + }, + { + "annotatedType": "io.helidon.scheduling.FixedRateConfig", + "type": "io.helidon.scheduling.FixedRate", + "inherits": [ + "io.helidon.scheduling.TaskConfig" + ], + "producers": [ + "io.helidon.scheduling.FixedRateConfig#create(io.helidon.common.config.Config)", + "io.helidon.scheduling.FixedRateConfig#builder()", + "io.helidon.scheduling.FixedRate#create(io.helidon.scheduling.FixedRateConfig)" + ], + "options": [ + { + "description": "Fixed rate delay between each invocation. Time unit is by default java.util.concurrent.TimeUnit#SECONDS,\n can be specified with io.helidon.scheduling.FixedRateConfig.Builder#timeUnit(java.util.concurrent.TimeUnit).\n\n @return delay between each invocation", + "key": "delay", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#delay(long)", + "type": "java.lang.Long", + "required": true + }, + { + "defaultValue": "SINCE_PREVIOUS_START", + "description": "Configure whether the delay between the invocations should be calculated from the time when previous task started or ended.\n Delay type is by default FixedRate.DelayType#SINCE_PREVIOUS_START.\n\n @return delay type", + "key": "delay-type", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#delayType(io.helidon.scheduling.FixedRate.DelayType)", + "type": "io.helidon.scheduling.FixedRate.DelayType", + "allowedValues": [ + { + "description": "Next invocation delay is measured from the previous invocation task start.", + "value": "SINCE_PREVIOUS_START" + }, + { + "description": "Next invocation delay is measured from the previous invocation task end.", + "value": "SINCE_PREVIOUS_END" + } + ] + }, + { + "defaultValue": "TimeUnit.SECONDS", + "description": "java.util.concurrent.TimeUnit TimeUnit used for interpretation of values provided with\n io.helidon.scheduling.FixedRateConfig.Builder#delay(long)\n and io.helidon.scheduling.FixedRateConfig.Builder#initialDelay(long).\n\n @return time unit for interpreting values\n in io.helidon.scheduling.FixedRateConfig.Builder#delay(long)\n and io.helidon.scheduling.FixedRateConfig.Builder#initialDelay(long)", + "key": "time-unit", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#timeUnit(java.util.concurrent.TimeUnit)", + "type": "java.util.concurrent.TimeUnit", + "allowedValues": [ + { + "description": "", + "value": "NANOSECONDS" + }, + { + "description": "", + "value": "MICROSECONDS" + }, + { + "description": "", + "value": "MILLISECONDS" + }, + { + "description": "", + "value": "SECONDS" + }, + { + "description": "", + "value": "MINUTES" + }, + { + "description": "", + "value": "HOURS" + }, + { + "description": "", + "value": "DAYS" + } + ] + }, + { + "defaultValue": "0", + "description": "Initial delay of the first invocation. Time unit is by default java.util.concurrent.TimeUnit#SECONDS,\n can be specified with\n io.helidon.scheduling.FixedRateConfig.Builder#timeUnit(java.util.concurrent.TimeUnit) timeUnit().\n\n @return initial delay value", + "key": "initial-delay", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#initialDelay(long)", + "type": "java.lang.Long" + } + ] + }, + { + "annotatedType": "io.helidon.scheduling.CronConfig", + "type": "io.helidon.scheduling.Cron", + "inherits": [ + "io.helidon.scheduling.TaskConfig" + ], + "producers": [ + "io.helidon.scheduling.CronConfig#create(io.helidon.common.config.Config)", + "io.helidon.scheduling.CronConfig#builder()", + "io.helidon.scheduling.Cron#create(io.helidon.scheduling.CronConfig)" + ], + "options": [ + { + "defaultValue": "true", + "description": "Allow concurrent execution if previous task didn't finish before next execution.\n Default value is `true`.\n\n @return true for allow concurrent execution.", + "key": "concurrent", + "method": "io.helidon.scheduling.CronConfig.Builder#concurrentExecution(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Cron expression for specifying period of execution.\n

    \n Examples:\n

      \n
    • `0/2 * * * * ? *` - Every 2 seconds
    • \n
    • `0 45 9 ? * *` - Every day at 9:45
    • \n
    • `0 15 8 ? * MON-FRI` - Every workday at 8:15
    • \n
    \n\n @return cron expression", + "key": "expression", + "method": "io.helidon.scheduling.CronConfig.Builder#expression(java.lang.String)", + "required": true + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.config.tests.config.metadata.meta.api", + "types": [ + { + "annotatedType": "io.helidon.config.tests.config.metadata.meta.api.MyBuilder", + "description": "builder", + "type": "io.helidon.config.tests.config.metadata.meta.api.MyTarget", + "inherits": [ + "io.helidon.config.tests.config.metadata.meta.api.AbstractBuilder" + ], + "producers": [ + "io.helidon.config.tests.config.metadata.meta.api.MyBuilder#build()" + ], + "options": [ + { + "defaultValue": "message", + "description": "message description", + "key": "message", + "method": "io.helidon.config.tests.config.metadata.meta.api.MyBuilder#message(java.lang.String)" + }, + { + "defaultValue": "42", + "description": "type description", + "key": "type", + "method": "io.helidon.config.tests.config.metadata.meta.api.MyBuilder#type(int)", + "type": "java.lang.Integer", + "allowedValues": [ + { + "description": "answer", + "value": "42" + }, + { + "description": "no answer", + "value": "0" + } + ] + } + ] + }, + { + "annotatedType": "io.helidon.config.tests.config.metadata.meta.api.AbstractBuilder", + "description": "abstract builder", + "type": "io.helidon.config.tests.config.metadata.meta.api.AbstractBuilder", + "options": [ + { + "description": "abstract description", + "key": "abstract-message", + "method": "io.helidon.config.tests.config.metadata.meta.api.AbstractBuilder#abstractMessage(java.lang.String)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.config.tests.config.metadata.builder.api", + "types": [ + { + "annotatedType": "io.helidon.config.tests.config.metadata.builder.api.MyAbstract", + "description": "abstract builder", + "type": "io.helidon.config.tests.config.metadata.builder.api.MyAbstract", + "producers": [ + "io.helidon.config.tests.config.metadata.builder.api.MyAbstract#create(io.helidon.common.config.Config)", + "io.helidon.config.tests.config.metadata.builder.api.MyAbstract#builder()" + ], + "options": [ + { + "description": "abstract description", + "key": "abstract-message", + "method": "io.helidon.config.tests.config.metadata.builder.api.MyAbstract.Builder#abstractMessage(java.lang.String)" + } + ] + }, + { + "annotatedType": "io.helidon.config.tests.config.metadata.builder.api.MyTarget", + "description": "builder", + "type": "io.helidon.config.tests.config.metadata.builder.api.MyTarget", + "inherits": [ + "io.helidon.config.tests.config.metadata.builder.api.MyAbstract" + ], + "producers": [ + "io.helidon.config.tests.config.metadata.builder.api.MyTarget#create(io.helidon.common.config.Config)", + "io.helidon.config.tests.config.metadata.builder.api.MyTarget#builder()" + ], + "options": [ + { + "defaultValue": "message", + "description": "message description", + "key": "message", + "method": "io.helidon.config.tests.config.metadata.builder.api.MyTarget.Builder#message(java.lang.String)" + }, + { + "defaultValue": "42", + "description": "type description", + "key": "type", + "method": "io.helidon.config.tests.config.metadata.builder.api.MyTarget.Builder#type(int)", + "type": "java.lang.Integer", + "allowedValues": [ + { + "description": "answer", + "value": "42" + }, + { + "description": "no answer", + "value": "0" + } + ] + } + ] + } + ] + } +] +[ + { + "module": "microprofile.config.api", + "types": [ + { + "annotatedType": "io.helidon.config.mp.MpConfigBuilder", + "prefix": "mp.config", + "type": "org.eclipse.microprofile.config.Config", + "standalone": true, + "producers": [ + "io.helidon.config.mp.MpConfigBuilder#build()" + ], + "options": [ + { + "description": "Configure an explicit profile name.", + "key": "profile", + "method": "io.helidon.config.mp.MpConfigBuilder#profile(java.lang.String)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.util", + "types": [ + { + "annotatedType": "io.helidon.security.util.TokenHandler.Builder", + "type": "io.helidon.security.util.TokenHandler", + "producers": [ + "io.helidon.security.util.TokenHandler.Builder#build()", + "io.helidon.security.util.TokenHandler#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Set the token pattern (Regular expression) to extract the token.", + "key": "regexp", + "method": "io.helidon.security.util.TokenHandler.Builder#tokenPattern(java.util.regex.Pattern)" + }, + { + "description": "Set the prefix of header value to extract the token.", + "key": "prefix", + "method": "io.helidon.security.util.TokenHandler.Builder#tokenPrefix(java.lang.String)" + }, + { + "description": "Set the name of header to look into to extract the token.", + "key": "header", + "method": "io.helidon.security.util.TokenHandler.Builder#tokenHeader(java.lang.String)" + }, + { + "description": "Token format for creating outbound tokens.", + "key": "format", + "method": "io.helidon.security.util.TokenHandler.Builder#tokenFormat(java.lang.String)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security", + "types": [ + { + "annotatedType": "io.helidon.security.Security.Builder", + "description": "Configuration of security providers, integration and other security options", + "prefix": "security", + "type": "io.helidon.security.Security", + "standalone": true, + "producers": [ + "io.helidon.security.Security.Builder#build()", + "io.helidon.security.Security#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "defaultValue": "FIRST", + "description": "Type of the policy.", + "key": "provider-policy.type", + "method": "io.helidon.security.Security.Builder#providerSelectionPolicy(java.util.function.Function)", + "type": "io.helidon.security.ProviderSelectionPolicyType", + "allowedValues": [ + { + "description": "Choose first provider from the list by default.\n Choose provider with the name defined when explicit provider requested.", + "value": "FIRST" + }, + { + "description": "Can compose multiple providers together to form a single\n logical provider.", + "value": "COMPOSITE" + }, + { + "description": "Explicit class for a custom ProviderSelectionPolicyType.", + "value": "CLASS" + } + ] + }, + { + "description": "ID of the default authentication provider", + "key": "default-authentication-provider", + "method": "io.helidon.security.Security.Builder#authenticationProvider(io.helidon.security.spi.AuthenticationProvider)", + "providerType": "java.lang.String", + "provider": true + }, + { + "description": "Name of the secret provider", + "key": "secrets.*.provider", + "method": "io.helidon.security.Security.Builder#addSecret(java.lang.String, io.helidon.security.spi.SecretsProvider, T)" + }, + { + "description": "Provider selection policy class name, only used when type is set to CLASS", + "key": "provider-policy.class-name", + "method": "io.helidon.security.Security.Builder#providerSelectionPolicy(java.util.function.Function)", + "type": "java.lang.Class" + }, + { + "description": "Configuration specific to the secret provider", + "key": "secrets.*.config", + "method": "io.helidon.security.Security.Builder#addSecret(java.lang.String, io.helidon.security.spi.SecretsProvider, T)", + "providerType": "io.helidon.security.SecretsProviderConfig", + "type": "io.helidon.security.SecretsProviderConfig", + "provider": true + }, + { + "defaultValue": "true", + "description": "Whether or not tracing should be enabled. If set to false, security tracer will be a no-op tracer.", + "key": "tracing.enabled", + "method": "io.helidon.security.Security.Builder#tracingEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Server time to use when evaluating security policies that depend on time.", + "key": "environment.server-time", + "method": "io.helidon.security.Security.Builder#serverTime(io.helidon.security.SecurityTime)", + "type": "io.helidon.security.SecurityTime" + }, + { + "description": "ID of the default authorization provider", + "key": "default-authorization-provider", + "method": "io.helidon.security.Security.Builder#authorizationProvider(io.helidon.security.spi.AuthorizationProvider)" + }, + { + "description": "Configured secrets", + "key": "secrets", + "kind": "LIST", + "method": "io.helidon.security.Security.Builder#addSecret(java.lang.String, io.helidon.security.spi.SecretsProvider, T)", + "type": "io.helidon.common.config.Config" + }, + { + "description": "Add a provider, works as #addProvider(io.helidon.security.spi.SecurityProvider, String), where the name is set\n to {@link\n Class#getSimpleName()}.", + "key": "providers", + "kind": "LIST", + "method": "io.helidon.security.Security.Builder#addProvider(io.helidon.security.spi.SecurityProvider)", + "providerType": "io.helidon.security.spi.SecurityProvider", + "type": "io.helidon.security.spi.SecurityProvider", + "provider": true, + "required": true + }, + { + "defaultValue": "true", + "description": "Security can be disabled using configuration, or explicitly.\n By default, security instance is enabled.\n Disabled security instance will not perform any checks and allow\n all requests.", + "key": "enabled", + "method": "io.helidon.security.Security.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Name of the secret, used for lookup", + "key": "secrets.*.name", + "method": "io.helidon.security.Security.Builder#addSecret(java.lang.String, io.helidon.security.spi.SecretsProvider, T)" + } + ] + }, + { + "annotatedType": "io.helidon.security.SecurityTime.Builder", + "type": "io.helidon.security.SecurityTime", + "producers": [ + "io.helidon.security.SecurityTime.Builder#build()", + "io.helidon.security.SecurityTime#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "defaultValue": "0", + "description": "Configure a time-shift in seconds, to move the current time to past or future.", + "key": "shift-by-seconds", + "method": "io.helidon.security.SecurityTime.Builder#shiftBySeconds(long)", + "type": "java.lang.Long" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "year", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "millisecond", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "minute", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "second", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + }, + { + "description": "Override current time zone. The time will represent the SAME instant, in an explicit timezone.\n

    \n If we are in a UTC time zone and you set the timezone to \"Europe/Prague\", the time will be shifted by the offset\n of Prague (e.g. if it is noon right now in UTC, you would get 14:00).", + "key": "time-zone", + "method": "io.helidon.security.SecurityTime.Builder#timeZone(java.time.ZoneId)", + "type": "java.time.ZoneId" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "month", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "day-of-month", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + }, + { + "description": "Set an explicit value for one of the time fields (such as ChronoField#YEAR).", + "key": "hour-of-day", + "method": "io.helidon.security.SecurityTime.Builder#value(java.time.temporal.ChronoField, long)", + "type": "java.lang.Long" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.jwt", + "types": [ + { + "annotatedType": "io.helidon.security.providers.jwt.JwtProvider.Builder", + "description": "JWT authentication provider", + "prefix": "jwt", + "type": "io.helidon.security.providers.jwt.JwtProvider", + "producers": [ + "io.helidon.security.providers.jwt.JwtProvider.Builder#build()", + "io.helidon.security.providers.jwt.JwtProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.AuthenticationProvider" + ], + "options": [ + { + "defaultValue": "true", + "description": "Whether to authenticate requests.", + "key": "authenticate", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#authenticate(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "JWK resource used to verify JWTs created by other parties.", + "key": "atn-token.jwk.resource", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#verifyJwk(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "defaultValue": "true", + "description": "Claim `groups` from JWT will be used to automatically add\n groups to current subject (may be used with jakarta.annotation.security.RolesAllowed annotation).", + "key": "use-jwt-groups", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#useJwtGroups(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Whether to allow impersonation by explicitly overriding\n username from outbound requests using io.helidon.security.EndpointConfig#PROPERTY_OUTBOUND_ID\n property.\n By default this is not allowed and identity can only be propagated.", + "key": "allow-impersonation", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#allowImpersonation(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Issuer used to create new JWTs.", + "key": "sign-token.jwt-issuer", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#issuer(java.lang.String)" + }, + { + "defaultValue": "USER", + "description": "Principal type this provider extracts (and also propagates).", + "key": "principal-type", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#subjectType(io.helidon.security.SubjectType)", + "type": "io.helidon.security.SubjectType", + "allowedValues": [ + { + "description": "", + "value": "USER" + }, + { + "description": "", + "value": "SERVICE" + } + ] + }, + { + "description": "JWK resource used to sign JWTs created by us.", + "key": "sign-token.jwk.resource", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#signJwk(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "defaultValue": "false", + "description": "Whether authentication is required.\n By default, request will fail if the username cannot be extracted.\n If set to false, request will process and this provider will abstain.", + "key": "optional", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Whether to propagate identity.", + "key": "propagate", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#propagate(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Configure support for unsigned JWT.\n If this is set to `true` any JWT that has algorithm\n set to `none` and no `kid` defined will be accepted.\n Note that this has serious security impact - if JWT can be sent\n from a third party, this allows the third party to send ANY JWT\n and it would be accpted as valid.", + "key": "allow-unsigned", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#allowUnsigned(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Token handler to extract username from request.", + "key": "atn-token.handler", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#atnTokenHandler(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + }, + { + "defaultValue": "true", + "description": "Configure whether to verify signatures.\n Signatures verification is enabled by default. You can configure the provider\n not to verify signatures.\n

    \n Make sure your service is properly secured on network level and only\n accessible from a secure endpoint that provides the JWTs when signature verification\n is disabled. If signature verification is disabled, this service will accept ANY JWT", + "key": "atn-token.verify-signature", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#verifySignature(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Audience expected in inbound JWTs.", + "key": "atn-token.jwt-audience", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#expectedAudience(java.lang.String)" + }, + { + "description": "Configuration of outbound rules.", + "key": "sign-token", + "method": "io.helidon.security.providers.jwt.JwtProvider.Builder#outboundConfig(io.helidon.security.providers.common.OutboundConfig)", + "type": "io.helidon.security.providers.common.OutboundConfig" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.httpsign", + "types": [ + { + "annotatedType": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder", + "description": "HTTP header signature provider.", + "prefix": "http-signatures", + "type": "io.helidon.security.providers.httpsign.HttpSignProvider", + "producers": [ + "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#build()", + "io.helidon.security.providers.httpsign.HttpSignProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.AuthenticationProvider" + ], + "options": [ + { + "description": "Add a header that is validated on inbound requests. Provider may support more than\n one header to validate.", + "key": "headers", + "kind": "LIST", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#addAcceptHeader(io.helidon.security.providers.httpsign.HttpSignHeader)", + "type": "io.helidon.security.providers.httpsign.HttpSignHeader", + "allowedValues": [ + { + "description": "Creates (or validates) a \"Signature\" header.", + "value": "SIGNATURE" + }, + { + "description": "Creates (or validates) an \"Authorization\" header, that contains \"Signature\" as the\n beginning of its content (the rest of the header is the same as for #SIGNATURE.", + "value": "AUTHORIZATION" + }, + { + "description": "Custom provided using a io.helidon.security.util.TokenHandler.", + "value": "CUSTOM" + } + ] + }, + { + "defaultValue": "true", + "description": "Set whether the signature is optional. If set to true (default), this provider will\n SecurityResponse.SecurityStatus#ABSTAIN from this request if signature is not\n present. If set to false, this provider will SecurityResponse.SecurityStatus#FAILURE fail\n if signature is not present.", + "key": "optional", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Enable support for Helidon versions before 3.0.0 (exclusive).\n

    \n Until version 3.0.0 (exclusive) there was a trailing end of line added to the signed\n data.\n To be able to communicate cross versions, we must configure this when talking to older versions of Helidon.\n Default value is `false`. In Helidon 2.x, this switch exists as well and the default is `true`, to\n allow communication between versions as needed.", + "key": "backward-compatible-eol", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#backwardCompatibleEol(java.lang.Boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Add outbound targets to this builder.\n The targets are used to chose what to do for outbound communication.\n The targets should have OutboundTargetDefinition attached through\n OutboundTarget.Builder#customObject(Class, Object) to tell us how to sign\n the request.\n

    \n The same can be done through configuration:\n

    \n {\n  name = \"http-signatures\"\n  class = \"HttpSignProvider\"\n  http-signatures {\n      targets: [\n      {\n          name = \"service2\"\n          hosts = [\"localhost\"]\n          paths = [\"/service2/.*\"]\n\n          # This configures the OutboundTargetDefinition\n          signature {\n              key-id = \"service1\"\n              hmac.secret = \"${CLEAR=password}\"\n          }\n      }]\n  }\n }\n 
    ", + "key": "outbound", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#outbound(io.helidon.security.providers.common.OutboundConfig)", + "type": "io.helidon.security.providers.common.OutboundConfig" + }, + { + "description": "Add inbound configuration. This is used to validate signature and authenticate the\n party.\n

    \n The same can be done through configuration:\n

    \n {\n  name = \"http-signatures\"\n  class = \"HttpSignProvider\"\n  http-signatures {\n      inbound {\n          # This configures the InboundClientDefinition\n          keys: [\n          {\n              key-id = \"service1\"\n              hmac.secret = \"${CLEAR=password}\"\n          }]\n      }\n  }\n }\n 
    ", + "key": "inbound.keys", + "kind": "LIST", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#addInbound(io.helidon.security.providers.httpsign.InboundClientDefinition)", + "type": "io.helidon.security.providers.httpsign.InboundClientDefinition" + }, + { + "defaultValue": "helidon", + "description": "Realm to use for challenging inbound requests that do not have \"Authorization\" header\n in case header is HttpSignHeader#AUTHORIZATION and singatures are not optional.", + "key": "realm", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#realm(java.lang.String)" + }, + { + "description": "Override the default inbound required headers (e.g. headers that MUST be signed and\n headers that MUST be signed IF present).\n

    \n Defaults:\n

      \n
    • get, head, delete methods: date, (request-target), host are mandatory; authorization if present (unless we are\n creating/validating the HttpSignHeader#AUTHORIZATION ourselves
    • \n
    • put, post: same as above, with addition of: content-length, content-type and digest if present\n
    • for other methods: date, (request-target)
    • \n
    \n Note that this provider DOES NOT validate the \"Digest\" HTTP header, only the signature.", + "key": "sign-headers", + "kind": "LIST", + "method": "io.helidon.security.providers.httpsign.HttpSignProvider.Builder#inboundRequiredHeaders(io.helidon.security.providers.httpsign.SignedHeadersConfig)", + "type": "io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig" + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder", + "type": "io.helidon.security.providers.httpsign.InboundClientDefinition", + "producers": [ + "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#build()", + "io.helidon.security.providers.httpsign.InboundClientDefinition#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "The key id of this client to map to this signature validation configuration.", + "key": "key-id", + "method": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#keyId(java.lang.String)" + }, + { + "description": "For algorithms based on public/private key (such as rsa-sha256), this provides access to the public key of the client.", + "key": "public-key", + "method": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#publicKeyConfig(io.helidon.common.pki.Keys)", + "type": "io.helidon.common.pki.Keys" + }, + { + "description": "Helper method to configure a password-like secret (instead of byte based #hmacSecret(byte[]).\n The password is transformed to bytes with StandardCharsets#UTF_8 charset.", + "key": "hmac.secret", + "method": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#hmacSecret(java.lang.String)" + }, + { + "description": "The principal name of the client, defaults to keyId if not configured.", + "key": "principal-name", + "method": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#principalName(java.lang.String)" + }, + { + "description": "Algorithm of signature used by this client.\n Currently supported:\n
      \n
    • rsa-sha256 - asymmetric based on public/private keys
    • \n
    • hmac-sha256 - symmetric based on a shared secret
    • \n
    ", + "key": "algorithm", + "method": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#algorithm(java.lang.String)" + }, + { + "defaultValue": "SERVICE", + "description": "The type of principal we have authenticated (either user or service, defaults to service).", + "key": "principal-type", + "method": "io.helidon.security.providers.httpsign.InboundClientDefinition.Builder#subjectType(io.helidon.security.SubjectType)", + "type": "io.helidon.security.SubjectType", + "allowedValues": [ + { + "description": "", + "value": "USER" + }, + { + "description": "", + "value": "SERVICE" + } + ] + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig", + "type": "io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig", + "producers": [ + "io.helidon.security.providers.httpsign.SignedHeadersConfig.HeadersConfig#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Headers that must be signed (and signature validation or creation should fail if not signed or present)", + "key": "always", + "kind": "LIST" + }, + { + "description": "Headers that must be signed if present in request.", + "key": "if-present", + "kind": "LIST" + }, + { + "description": "HTTP method this header configuration is bound to. If not present, it is considered default header configuration.", + "key": "method" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.oidc.common", + "types": [ + { + "annotatedType": "io.helidon.security.providers.oidc.common.OidcConfig.Builder", + "description": "Open ID Connect configuration", + "type": "io.helidon.security.providers.oidc.common.OidcConfig", + "inherits": [ + "io.helidon.security.providers.oidc.common.BaseBuilder" + ], + "producers": [ + "io.helidon.security.providers.oidc.common.OidcConfig.Builder#build()", + "io.helidon.security.providers.oidc.common.OidcConfig#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "defaultValue": "true", + "description": "Whether to encrypt state cookie created by this microservice.\n Defaults to `true`.", + "key": "cookie-encryption-state-enabled", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionEnabledState(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Configurations of the tenants", + "key": "tenants", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#addTenantConfig(io.helidon.security.providers.oidc.common.TenantConfig)", + "type": "io.helidon.security.providers.oidc.common.TenantConfig" + }, + { + "defaultValue": "/oidc/redirect", + "description": "URI to register web server component on, used by the OIDC server to\n redirect authorization requests to after a user logs in or approves\n scopes.\n Note that usually the redirect URI configured here must be the\n same one as configured on OIDC server.\n\n

    \n Defaults to {@value #DEFAULT_REDIRECT_URI}", + "key": "redirect-uri", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#redirectUri(java.lang.String)" + }, + { + "defaultValue": "false", + "description": "Can be set to `true` to force the use of relative URIs in all requests,\n regardless of the presence or absence of proxies or no-proxy lists. By default,\n requests that use the Proxy will have absolute URIs. Set this flag to `true`\n if the host is unable to accept absolute URIs.\n Defaults to {@value #DEFAULT_RELATIVE_URIS}.", + "key": "relative-uris", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#relativeUris(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Master password for encryption/decryption of cookies. This must be configured to the same value on each microservice\n using the cookie.", + "key": "cookie-encryption-password", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionPassword(char[])", + "type": "char[]" + }, + { + "defaultValue": "/", + "description": "Path the cookie is valid for.\n Defaults to \"/\".", + "key": "cookie-path", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookiePath(java.lang.String)" + }, + { + "description": "When using cookie, used to set MaxAge attribute of the cookie, defining how long\n the cookie is valid.\n Not used by default.", + "key": "cookie-max-age-seconds", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieMaxAgeSeconds(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "false", + "description": "By default, the client should redirect to the identity server for the user to log in.\n This behavior can be overridden by setting redirect to false. When token is not present in the request, the client\n will not redirect and just return appropriate error response code.", + "key": "redirect", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#redirect(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Proxy host to use. When defined, triggers usage of proxy for HTTP requests.\n Setting to empty String has the same meaning as setting to null - disables proxy.", + "key": "proxy-host", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#proxyHost(java.lang.String)" + }, + { + "defaultValue": "false", + "description": "Whether to use a query parameter to send JWT token from application to this\n server.", + "key": "query-param-use", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#useParam(java.lang.Boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "HELIDON_TENANT", + "description": "The name of the cookie to use for the tenant name.\n Defaults to {@value #DEFAULT_TENANT_COOKIE_NAME}.", + "key": "cookie-name-tenant", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieTenantName(java.lang.String)" + }, + { + "defaultValue": "80", + "description": "Proxy port.\n Defaults to {@value DEFAULT_PROXY_PORT}", + "key": "proxy-port", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#proxyPort(int)", + "type": "java.lang.Integer" + }, + { + "description": "Domain the cookie is valid for.\n Not used by default.", + "key": "cookie-domain", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieDomain(java.lang.String)" + }, + { + "defaultValue": "http", + "description": "Proxy protocol to use when proxy is used.\n Defaults to {@value DEFAULT_PROXY_PROTOCOL}.", + "key": "proxy-protocol", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#proxyProtocol(java.lang.String)" + }, + { + "defaultValue": "false", + "description": "Whether to encrypt token cookie created by this microservice.\n Defaults to `false`.", + "key": "cookie-encryption-enabled", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "5", + "description": "Configure maximal number of redirects when redirecting to an OIDC provider within a single authentication\n attempt.\n

    \n Defaults to {@value #DEFAULT_MAX_REDIRECTS}", + "key": "max-redirects", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#maxRedirects(int)", + "type": "java.lang.Integer" + }, + { + "description": "Name of the encryption configuration available through Security#encrypt(String, byte[]) and\n Security#decrypt(String, String).\n If configured and encryption is enabled for any cookie,\n Security MUST be configured in global or current `io.helidon.common.context.Context` (this\n is done automatically in Helidon MP).", + "key": "cookie-encryption-name", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionName(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Whether id token signature check should be enabled.\n Signature check is enabled by default, and it is highly recommended to not change that.\n Change this setting only when you really know what you are doing, otherwise it could case security issues.", + "key": "id-token-signature-validation", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#idTokenSignatureValidation(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Full URI of this application that is visible from user browser.\n Used to redirect request back from identity server after successful login.", + "key": "frontend-uri", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#frontendUri(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Whether to check if current IP address matches the one access token was issued for.\n This check helps with cookie replay attack prevention.", + "key": "access-token-ip-check", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#accessTokenIpCheck(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "h_ra", + "description": "Configure the parameter used to store the number of attempts in redirect.\n

    \n Defaults to {@value #DEFAULT_ATTEMPT_PARAM}", + "key": "redirect-attempt-param", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#redirectAttemptParam(java.lang.String)" + }, + { + "defaultValue": "LAX", + "description": "When using cookie, used to set the SameSite cookie value. Can be\n \"Strict\" or \"Lax\".", + "key": "cookie-same-site", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieSameSite(io.helidon.http.SetCookie.SameSite)", + "type": "io.helidon.http.SetCookie.SameSite", + "allowedValues": [ + { + "description": "", + "value": "LAX" + }, + { + "description": "", + "value": "STRICT" + }, + { + "description": "", + "value": "NONE" + } + ] + }, + { + "defaultValue": "accessToken", + "description": "Name of a query parameter that contains the JWT access token when parameter is used.", + "key": "query-param-name", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#paramName(java.lang.String)" + }, + { + "description": "Assign cross-origin resource sharing settings.", + "key": "cors", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#crossOriginConfig(io.helidon.cors.CrossOriginConfig)", + "type": "io.helidon.cors.CrossOriginConfig" + }, + { + "defaultValue": "false", + "description": "Force HTTPS for redirects to identity provider.\n Defaults to `false`.", + "key": "force-https-redirects", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#forceHttpsRedirects(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "id_token", + "description": "Name of a query parameter that contains the JWT id token when parameter is used.", + "key": "query-id-token-param-name", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#idTokenParamName(java.lang.String)" + }, + { + "defaultValue": "JSESSIONID_3", + "description": "The name of the cookie to use for the state storage.\n Defaults to {@value #DEFAULT_STATE_COOKIE_NAME}.", + "key": "cookie-name-state", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieNameState(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Whether to encrypt refresh token cookie created by this microservice.\n Defaults to `true`.", + "key": "cookie-encryption-refresh-enabled", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionEnabledRefreshToken(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "When using cookie, if set to true, the HttpOnly attribute will be configured.\n Defaults to {@value OidcCookieHandler.Builder#DEFAULT_HTTP_ONLY}.", + "key": "cookie-http-only", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieHttpOnly(java.lang.Boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "A TokenHandler to\n process header containing a JWT.\n Default is \"Authorization\" header with a prefix \"bearer \".", + "key": "header-token", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#headerTokenHandler(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + }, + { + "defaultValue": "true", + "description": "Whether to expect JWT in a header field.", + "key": "header-use", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#useHeader(java.lang.Boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "When using cookie, if set to true, the Secure attribute will be configured.\n Defaults to false.", + "key": "cookie-secure", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieSecure(java.lang.Boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Whether to encrypt tenant name cookie created by this microservice.\n Defaults to `true`.", + "key": "cookie-encryption-tenant-enabled", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionEnabledTenantName(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Whether access token signature check should be enabled.\n Signature check is enabled by default, and it is highly recommended to not change that.\n Change this setting only when you really know what you are doing, otherwise it could case security issues.", + "key": "token-signature-validation", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#tokenSignatureValidation(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "JSESSIONID", + "description": "Name of the cookie to use.\n Defaults to {@value #DEFAULT_COOKIE_NAME}.", + "key": "cookie-name", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieName(java.lang.String)" + }, + { + "defaultValue": "h_tenant", + "description": "Name of a query parameter that contains the tenant name when the parameter is used.\n Defaults to #DEFAULT_TENANT_PARAM_NAME.", + "key": "query-param-tenant-name", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#paramTenantName(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Whether to encrypt id token cookie created by this microservice.\n Defaults to `true`.", + "key": "cookie-encryption-id-enabled", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieEncryptionEnabledIdToken(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "JSESSIONID_2", + "description": "Name of the cookie to use for id token.\n Defaults to {@value #DEFAULT_COOKIE_NAME}_2.\n\n This cookie is only used when logout is enabled, as otherwise it is not needed.\n Content of this cookie is encrypted.", + "key": "cookie-name-id-token", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieNameIdToken(java.lang.String)" + }, + { + "defaultValue": "JSESSIONID_3", + "description": "The name of the cookie to use for the refresh token.\n Defaults to {@value #DEFAULT_REFRESH_COOKIE_NAME}.", + "key": "cookie-name-refresh-token", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#cookieNameRefreshToken(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Whether to use cookie to store JWT between requests.\n Defaults to {@value #DEFAULT_COOKIE_USE}.", + "key": "cookie-use", + "method": "io.helidon.security.providers.oidc.common.OidcConfig.Builder#useCookie(java.lang.Boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.oidc.common.TenantConfig.Builder", + "description": "Open ID Connect tenant configuration", + "type": "io.helidon.security.providers.oidc.common.TenantConfig", + "inherits": [ + "io.helidon.security.providers.oidc.common.BaseBuilder" + ], + "producers": [ + "io.helidon.security.providers.oidc.common.TenantConfig.Builder#build()" + ], + "options": [ + { + "description": "Name of the tenant.", + "key": "name", + "method": "io.helidon.security.providers.oidc.common.TenantConfig.Builder#name(java.lang.String)", + "required": true + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.oidc.common.BaseBuilder", + "type": "io.helidon.security.providers.oidc.common.BaseBuilder", + "options": [ + { + "description": "URI of the identity server, base used to retrieve OIDC metadata.", + "key": "identity-uri", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#identityUri(java.net.URI)", + "type": "java.net.URI" + }, + { + "description": "Resource configuration for OIDC Metadata\n containing endpoints to various identity services, as well as information about the identity server.", + "key": "oidc-metadata.resource", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#oidcMetadata(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "description": "Endpoint to use to validate JWT.\n Either use this or set #signJwk(JwkKeys) or #signJwk(Resource).", + "key": "introspect-endpoint-uri", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#introspectEndpointUri(java.net.URI)", + "type": "java.net.URI" + }, + { + "defaultValue": "false", + "description": "Allow audience claim to be optional.", + "key": "optional-audience", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#optionalAudience(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Audience of issued tokens.", + "key": "audience", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#audience(java.lang.String)" + }, + { + "defaultValue": "30000", + "description": "Timeout of calls using web client.", + "key": "client-timeout-millis", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#clientTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "A resource pointing to JWK with public keys of signing certificates used\n to validate JWT.", + "key": "sign-jwk.resource", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#signJwk(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "defaultValue": "openid", + "description": "Configure base scopes.\n By default, this is {@value #DEFAULT_BASE_SCOPES}.\n If scope has a qualifier, it must be used here.", + "key": "base-scopes", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#baseScopes(java.lang.String)" + }, + { + "defaultValue": "@default", + "description": "Configure one of the supported types of identity servers.\n\n If the type does not have an explicit mapping, a warning is logged and the default implementation is used.", + "key": "server-type", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#serverType(java.lang.String)" + }, + { + "description": "Issuer of issued tokens.", + "key": "issuer", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#issuer(java.lang.String)" + }, + { + "defaultValue": "false", + "description": "Configure audience claim check.", + "key": "check-audience", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#checkAudience(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Use JWK (a set of keys to validate signatures of JWT) to validate tokens.\n Use this method when you want to use default values for JWK or introspection endpoint URI.", + "key": "validate-jwt-with-jwk", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#validateJwtWithJwk(java.lang.Boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "If set to true, metadata will be loaded from default (well known)\n location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded\n even if oidc-metadata-resource is not defined. In such a case all URIs must be explicitly defined (e.g.\n token-endpoint-uri).", + "key": "oidc-metadata-well-known", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#oidcMetadataWellKnown(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Client secret as generated by OIDC server.\n Used to authenticate this application with the server when requesting\n JWT based on a code.", + "key": "client-secret", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#clientSecret(java.lang.String)" + }, + { + "description": "Audience of the scope required by this application. This is prefixed to\n the scope name when requesting scopes from the identity server.\n Defaults to empty string.", + "key": "scope-audience", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#scopeAudience(java.lang.String)" + }, + { + "defaultValue": "CLIENT_SECRET_BASIC", + "description": "Type of authentication to use when invoking the token endpoint.\n Current supported options:\n

      \n
    • io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_BASIC
    • \n
    • io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_POST
    • \n
    • io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#NONE
    • \n
    ", + "key": "token-endpoint-auth", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#tokenEndpointAuthentication(io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication)", + "type": "io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication", + "allowedValues": [ + { + "description": "Clients that have received a client_secret value from the Authorization Server authenticate with the Authorization\n Server in accordance with Section 2.3.1 of OAuth 2.0 [RFC6749] using the HTTP Basic authentication scheme.\n This is the default client authentication.", + "value": "CLIENT_SECRET_BASIC" + }, + { + "description": "Clients that have received a client_secret value from the Authorization Server, authenticate with the Authorization\n Server in accordance with Section 2.3.1 of OAuth 2.0 [RFC6749] by including the Client Credentials in the request body.", + "value": "CLIENT_SECRET_POST" + }, + { + "description": "Clients that have received a client_secret value from the Authorization Server create a JWT using an HMAC SHA\n algorithm, such as HMAC SHA-256. The HMAC (Hash-based Message Authentication Code) is calculated using the octets of\n the UTF-8 representation of the client_secret as the shared key.\n The Client authenticates in accordance with JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and\n Authorization Grants [OAuth.JWT] and Assertion Framework for OAuth 2.0 Client Authentication and Authorization\n Grants [OAuth.Assertions].\n

    \n The JWT MUST contain the following REQUIRED Claim Values and MAY contain the following\n OPTIONAL Claim Values.\n

    \n Required:\n `iss, sub, aud, jti, exp`\n

    \n Optional:\n `iat`", + "value": "CLIENT_SECRET_JWT" + }, + { + "description": "Clients that have registered a public key sign a JWT using that key. The Client authenticates in accordance with\n JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants [OAuth.JWT] and Assertion\n Framework for OAuth 2.0 Client Authentication and Authorization Grants [OAuth.Assertions].\n

    \n The JWT MUST contain the following REQUIRED Claim Values and MAY contain the following\n OPTIONAL Claim Values.\n

    \n Required:\n `iss, sub, aud, jti, exp`\n

    \n Optional:\n `iat`", + "value": "PRIVATE_KEY_JWT" + }, + { + "description": "The Client does not authenticate itself at the Token Endpoint, either because it uses only the Implicit Flow (and so\n does not use the Token Endpoint) or because it is a Public Client with no Client Secret or other authentication\n mechanism.", + "value": "NONE" + } + ] + }, + { + "description": "Client ID as generated by OIDC server.", + "key": "client-id", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#clientId(java.lang.String)" + }, + { + "description": "URI of an authorization endpoint used to redirect users to for logging-in.\n\n If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined\n an attempt is made to use #identityUri(URI)/oauth2/v1/authorize.", + "key": "authorization-endpoint-uri", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#authorizationEndpointUri(java.net.URI)", + "type": "java.net.URI" + }, + { + "description": "URI of a token endpoint used to obtain a JWT based on the authentication\n code.\n If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined\n an attempt is made to use #identityUri(URI)/oauth2/v1/token.", + "key": "token-endpoint-uri", + "method": "io.helidon.security.providers.oidc.common.BaseBuilder#tokenEndpointUri(java.net.URI)", + "type": "java.net.URI" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.idcs.mapper", + "types": [ + { + "annotatedType": "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider.Builder", + "description": "Multitenant IDCS role mapping provider", + "prefix": "idcs-role-mapper", + "type": "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider", + "inherits": [ + "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder" + ], + "producers": [ + "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider.Builder#build()", + "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.SubjectMappingProvider" + ], + "options": [ + { + "description": "Use explicit io.helidon.security.providers.common.EvictableCache for role caching.", + "key": "cache-config", + "method": "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider.Builder#cache(io.helidon.security.providers.common.EvictableCache>)", + "type": "io.helidon.security.providers.common.EvictableCache" + }, + { + "description": "Configure token handler for IDCS Application name.\n By default the header {@value IdcsMtRoleMapperProvider#IDCS_APP_HEADER} is used.", + "key": "idcs-app-name-handler", + "method": "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider.Builder#idcsAppNameTokenHandler(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + }, + { + "description": "Configure token handler for IDCS Tenant ID.\n By default the header {@value IdcsMtRoleMapperProvider#IDCS_TENANT_HEADER} is used.", + "key": "idcs-tenant-handler", + "method": "io.helidon.security.providers.idcs.mapper.IdcsMtRoleMapperProvider.Builder#idcsTenantTokenHandler(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder", + "type": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder", + "options": [ + { + "description": "Use explicit io.helidon.security.providers.oidc.common.OidcConfig instance, e.g. when using it also for OIDC\n provider.", + "key": "oidc-config", + "method": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder#oidcConfig(io.helidon.security.providers.oidc.common.OidcConfig)", + "type": "io.helidon.security.providers.oidc.common.OidcConfig" + }, + { + "defaultValue": "user", + "description": "Configure subject type to use when requesting roles from IDCS.\n Can be either #IDCS_SUBJECT_TYPE_USER or #IDCS_SUBJECT_TYPE_CLIENT.\n Defaults to #IDCS_SUBJECT_TYPE_USER.", + "key": "default-idcs-subject-type", + "method": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder#defaultIdcsSubjectType(java.lang.String)" + }, + { + "defaultValue": "USER", + "description": "Add a supported subject type.\n If none added, io.helidon.security.SubjectType#USER is used.\n If any added, only the ones added will be used (e.g. if you want to use\n both io.helidon.security.SubjectType#USER and io.helidon.security.SubjectType#SERVICE,\n both need to be added.", + "key": "subject-types", + "kind": "LIST", + "method": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder#addSubjectType(io.helidon.security.SubjectType)", + "type": "io.helidon.security.SubjectType", + "allowedValues": [ + { + "description": "", + "value": "USER" + }, + { + "description": "", + "value": "SERVICE" + } + ] + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider.Builder", + "description": "IDCS role mapping provider", + "prefix": "idcs-role-mapper", + "type": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider", + "inherits": [ + "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProviderBase.Builder" + ], + "producers": [ + "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider.Builder#build()", + "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.SubjectMappingProvider" + ], + "options": [ + { + "description": "Use explicit io.helidon.security.providers.common.EvictableCache for role caching.", + "key": "cache-config", + "method": "io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider.Builder#roleCache(io.helidon.security.providers.common.EvictableCache>)", + "type": "io.helidon.security.providers.common.EvictableCache" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.oidc", + "types": [ + { + "annotatedType": "io.helidon.security.providers.oidc.OidcProvider.Builder", + "description": "Open ID Connect security provider", + "prefix": "oidc", + "type": "io.helidon.security.providers.oidc.OidcProvider", + "producers": [ + "io.helidon.security.providers.oidc.OidcProvider.Builder#build()", + "io.helidon.security.providers.oidc.OidcProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.AuthenticationProvider", + "io.helidon.security.spi.SecurityProvider" + ], + "options": [ + { + "description": "Configuration of outbound rules.", + "key": "outbound-config", + "method": "io.helidon.security.providers.oidc.OidcProvider.Builder#outboundConfig(io.helidon.security.providers.common.OutboundConfig)", + "type": "io.helidon.security.providers.common.OutboundConfig", + "merge": true + }, + { + "description": "Configuration of OIDC (Open ID Connect).", + "key": "oidc-config", + "method": "io.helidon.security.providers.oidc.OidcProvider.Builder#oidcConfig(io.helidon.security.providers.oidc.common.OidcConfig)", + "type": "io.helidon.security.providers.oidc.common.OidcConfig", + "merge": true + }, + { + "defaultValue": "false", + "description": "Whether authentication is required.\n By default, request will fail if the authentication cannot be verified.\n If set to true, request will process and this provider will abstain.", + "key": "optional", + "method": "io.helidon.security.providers.oidc.OidcProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Claim `groups` from JWT will be used to automatically add\n groups to current subject (may be used with jakarta.annotation.security.RolesAllowed annotation).", + "key": "use-jwt-groups", + "method": "io.helidon.security.providers.oidc.OidcProvider.Builder#useJwtGroups(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Whether to propagate identity.", + "key": "propagate", + "method": "io.helidon.security.providers.oidc.OidcProvider.Builder#propagate(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.abac", + "types": [ + { + "annotatedType": "io.helidon.security.providers.abac.AbacProvider.Builder", + "description": "Attribute Based Access Control provider", + "prefix": "abac", + "type": "io.helidon.security.providers.abac.AbacProvider", + "producers": [ + "io.helidon.security.providers.abac.AbacProvider.Builder#build()", + "io.helidon.security.providers.abac.AbacProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.AuthorizationProvider" + ], + "options": [ + { + "defaultValue": "true", + "description": "Whether to fail if any attribute is left unvalidated.", + "key": "fail-on-unvalidated", + "method": "io.helidon.security.providers.abac.AbacProvider.Builder#failOnUnvalidated(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Whether to fail if NONE of the attributes is validated.", + "key": "fail-if-none-validated", + "method": "io.helidon.security.providers.abac.AbacProvider.Builder#failIfNoneValidated(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.common", + "types": [ + { + "annotatedType": "io.helidon.security.providers.common.OutboundConfig.Builder", + "description": "Outbound configuration for outbound security", + "type": "io.helidon.security.providers.common.OutboundConfig", + "producers": [ + "io.helidon.security.providers.common.OutboundConfig.Builder#build()", + "io.helidon.security.providers.common.OutboundConfig#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Add a new target configuration.", + "key": "outbound", + "kind": "LIST", + "method": "io.helidon.security.providers.common.OutboundConfig.Builder#addTarget(io.helidon.security.providers.common.OutboundTarget)", + "type": "io.helidon.security.providers.common.OutboundTarget" + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.common.EvictableCache.Builder", + "type": "io.helidon.security.providers.common.EvictableCache", + "producers": [ + "io.helidon.security.providers.common.EvictableCache.Builder#build()", + "io.helidon.security.providers.common.EvictableCache#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "defaultValue": "true", + "description": "If the cacheEnabled is set to false, no caching will be done.\n Otherwise (default behavior) evictable caching will be used.", + "key": "cache-enabled", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#cacheEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Configure evictor to check if a record is still valid.\n This should be a fast way to check, as it is happening in a ConcurrentHashMap#forEachKey(long, Consumer).\n This is also called during all get and remove operations to only return valid records.", + "key": "evictor-class", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#evictor(java.util.function.BiFunction)", + "type": "java.lang.Class" + }, + { + "defaultValue": "10000", + "description": "Configure parallelism threshold.", + "key": "parallelism-threshold", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#parallelismThreshold(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "3600000", + "description": "Configure record timeout since last access.", + "key": "cache-timeout-millis", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#timeout(long, java.util.concurrent.TimeUnit)", + "type": "java.lang.Long" + }, + { + "defaultValue": "60000", + "description": "Delay from the creation of the cache to first eviction", + "key": "cache-evict-delay-millis", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#evictSchedule(long, long, java.util.concurrent.TimeUnit)", + "type": "java.lang.Long" + }, + { + "defaultValue": "300000", + "description": "How often to evict records", + "key": "cache-evict-period-millis", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#evictSchedule(long, long, java.util.concurrent.TimeUnit)", + "type": "java.lang.Long" + }, + { + "defaultValue": "100000", + "description": "Configure maximal cache size.", + "key": "max-size", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#maxSize(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "3600000", + "description": "Configure record timeout since its creation.", + "key": "cache-overall-timeout-millis", + "method": "io.helidon.security.providers.common.EvictableCache.Builder#overallTimeout(long, java.util.concurrent.TimeUnit)", + "type": "java.lang.Long" + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.common.OutboundTarget.Builder", + "type": "io.helidon.security.providers.common.OutboundTarget", + "producers": [ + "io.helidon.security.providers.common.OutboundTarget.Builder#build()", + "io.helidon.security.providers.common.OutboundTarget#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Add supported paths for this target. May be called more than once to add more paths.\n The path is tested as is against called path, and also tested as a regular expression.", + "key": "paths", + "kind": "LIST", + "method": "io.helidon.security.providers.common.OutboundTarget.Builder#addPath(java.lang.String)" + }, + { + "description": "Add supported method for this target. May be called more than once to add more methods.\n The method is tested as is ignoring case against the used method.", + "key": "methods", + "kind": "LIST", + "method": "io.helidon.security.providers.common.OutboundTarget.Builder#addMethod(java.lang.String)" + }, + { + "description": "Add supported host for this target. May be called more than once to add more hosts.\n

    \n Valid examples:\n

      \n
    • localhost\n
    • www.google.com\n
    • 127.0.0.1\n
    • *.oracle.com\n
    • 192.169.*.*\n
    • *.google.*\n
    ", + "key": "hosts", + "kind": "LIST", + "method": "io.helidon.security.providers.common.OutboundTarget.Builder#addHost(java.lang.String)" + }, + { + "description": "Configure the name of this outbound target.", + "key": "name", + "method": "io.helidon.security.providers.common.OutboundTarget.Builder#name(java.lang.String)", + "required": true + }, + { + "description": "Add supported transports for this target. May be called more than once to add more transports.\n

    \n Valid examples:\n

      \n
    • http\n
    • https\n
    \n There is no wildcard support", + "key": "transport", + "kind": "LIST", + "method": "io.helidon.security.providers.common.OutboundTarget.Builder#addTransport(java.lang.String)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.google.login", + "types": [ + { + "annotatedType": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder", + "description": "Google Authentication provider", + "prefix": "google-login", + "type": "io.helidon.security.providers.google.login.GoogleTokenProvider", + "producers": [ + "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#build()", + "io.helidon.security.providers.google.login.GoogleTokenProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.AuthenticationProvider" + ], + "options": [ + { + "defaultValue": "false", + "description": "If set to true, this provider will return io.helidon.security.SecurityResponse.SecurityStatus#ABSTAIN instead\n of failing in case of invalid request.", + "key": "optional", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Set proxy host when talking to Google.", + "key": "proxy-host", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#proxyHost(java.lang.String)" + }, + { + "description": "Outbound configuration - a set of outbound targets that\n will have the token propagated.", + "key": "outbound", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#outboundConfig(io.helidon.security.providers.common.OutboundConfig)", + "type": "io.helidon.security.providers.common.OutboundConfig" + }, + { + "defaultValue": "helidon", + "description": "Set the authentication realm to build challenge, defaults to \"helidon\".", + "key": "realm", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#realm(java.lang.String)" + }, + { + "description": "Google application client id, to validate that the token was generated by Google for us.", + "key": "client-id", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#clientId(java.lang.String)" + }, + { + "defaultValue": "80", + "description": "Set proxy port when talking to Google.", + "key": "proxy-port", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#proxyPort(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "`Authorization` header with `bearer` prefix", + "description": "Token provider to extract Google access token from request, defaults to \"Authorization\" header with a \"bearer \" prefix.", + "key": "token", + "method": "io.helidon.security.providers.google.login.GoogleTokenProvider.Builder#tokenProvider(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.httpauth", + "types": [ + { + "annotatedType": "io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser", + "type": "io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser", + "producers": [ + "io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "User's password", + "key": "password" + }, + { + "description": "List of roles the user is in", + "key": "roles", + "kind": "LIST" + }, + { + "description": "User's login", + "key": "login" + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder", + "description": "HTTP Basic Authentication provider", + "prefix": "http-basic-auth", + "type": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider", + "producers": [ + "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder#build()", + "io.helidon.security.providers.httpauth.HttpBasicAuthProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.AuthenticationProvider" + ], + "options": [ + { + "description": "Set user store to validate users.\n Removes any other stores added through #addUserStore(SecureUserStore).", + "key": "users", + "kind": "LIST", + "method": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder#userStore(io.helidon.security.providers.httpauth.SecureUserStore)", + "type": "io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser" + }, + { + "defaultValue": "false", + "description": "Whether authentication is required.\n By default, request will fail if the authentication cannot be verified.\n If set to false, request will process and this provider will abstain.", + "key": "optional", + "method": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Add a new outbound target to configure identity propagation or explicit username/password.", + "key": "outbound", + "kind": "LIST", + "method": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder#addOutboundTarget(io.helidon.security.providers.common.OutboundTarget)", + "type": "io.helidon.security.providers.common.OutboundTarget" + }, + { + "defaultValue": "helidon", + "description": "Set the realm to use when challenging users.", + "key": "realm", + "method": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder#realm(java.lang.String)" + }, + { + "defaultValue": "USER", + "description": "Principal type this provider extracts (and also propagates).", + "key": "principal-type", + "method": "io.helidon.security.providers.httpauth.HttpBasicAuthProvider.Builder#subjectType(io.helidon.security.SubjectType)", + "type": "io.helidon.security.SubjectType", + "allowedValues": [ + { + "description": "", + "value": "USER" + }, + { + "description": "", + "value": "SERVICE" + } + ] + } + ] + }, + { + "annotatedType": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder", + "description": "Http digest authentication security provider", + "prefix": "http-digest-auth", + "type": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider", + "producers": [ + "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#build()", + "io.helidon.security.providers.httpauth.HttpDigestAuthProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.AuthenticationProvider" + ], + "options": [ + { + "defaultValue": "NONE", + "description": "Only `AUTH` supported. If left empty, uses the legacy approach (older RFC version). `AUTH-INT` is not supported.", + "key": "qop", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#addDigestQop(io.helidon.security.providers.httpauth.HttpDigest.Qop)", + "type": "io.helidon.security.providers.httpauth.HttpDigest.Qop", + "allowedValues": [ + { + "description": "Legacy approach - used internally to parse headers. Do not use this option when\n building provider. If you want to support only legacy RFC, please use\n HttpDigestAuthProvider.Builder#noDigestQop().\n Only #AUTH is supported, as auth-int requires access to message body.", + "value": "NONE" + }, + { + "description": "QOP \"auth\" - stands for \"authentication\".", + "value": "AUTH" + } + ] + }, + { + "description": "Set user store to obtain passwords and roles based on logins.", + "key": "users", + "kind": "LIST", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#userStore(io.helidon.security.providers.httpauth.SecureUserStore)", + "type": "io.helidon.security.providers.httpauth.ConfigUserStore.ConfigUser" + }, + { + "defaultValue": "86400000", + "description": "How long will the nonce value be valid. When timed-out, browser will re-request username/password.", + "key": "nonce-timeout-millis", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#digestNonceTimeout(long, java.util.concurrent.TimeUnit)", + "type": "java.lang.Long" + }, + { + "description": "The nonce is encrypted using this secret - to make sure the nonce we get back was generated by us and to\n make sure we can safely time-out nonce values.\n This secret must be the same for all service instances (or all services that want to share the same authentication).\n Defaults to a random password - e.g. if deployed to multiple servers, the authentication WILL NOT WORK. You MUST\n provide your own password to work in a distributed environment with non-sticky load balancing.", + "key": "server-secret", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#digestServerSecret(char[])" + }, + { + "defaultValue": "false", + "description": "Whether authentication is required.\n By default, request will fail if the authentication cannot be verified.\n If set to false, request will process and this provider will abstain.", + "key": "optional", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "Helidon", + "description": "Set the realm to use when challenging users.", + "key": "realm", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#realm(java.lang.String)" + }, + { + "defaultValue": "MD5", + "description": "Digest algorithm to use.", + "key": "algorithm", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#digestAlgorithm(io.helidon.security.providers.httpauth.HttpDigest.Algorithm)", + "type": "io.helidon.security.providers.httpauth.HttpDigest.Algorithm", + "allowedValues": [ + { + "description": "MD5 algorithm.", + "value": "MD5" + } + ] + }, + { + "defaultValue": "USER", + "description": "Principal type this provider extracts (and also propagates).", + "key": "principal-type", + "method": "io.helidon.security.providers.httpauth.HttpDigestAuthProvider.Builder#subjectType(io.helidon.security.SubjectType)", + "type": "io.helidon.security.SubjectType", + "allowedValues": [ + { + "description": "", + "value": "USER" + }, + { + "description": "", + "value": "SERVICE" + } + ] + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.security.providers.header", + "types": [ + { + "annotatedType": "io.helidon.security.providers.header.HeaderAtnProvider.Builder", + "description": "Security provider that extracts a username (or service name) from a header.", + "prefix": "header-atn", + "type": "io.helidon.security.providers.header.HeaderAtnProvider", + "producers": [ + "io.helidon.security.providers.header.HeaderAtnProvider.Builder#build()", + "io.helidon.security.providers.header.HeaderAtnProvider#create(io.helidon.common.config.Config)" + ], + "provides": [ + "io.helidon.security.spi.SecurityProvider", + "io.helidon.security.spi.AuthenticationProvider" + ], + "options": [ + { + "defaultValue": "true", + "description": "Whether to authenticate requests.", + "key": "authenticate", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#authenticate(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Token handler to extract username from request.", + "key": "atn-token", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#atnTokenHandler(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + }, + { + "defaultValue": "false", + "description": "Whether authentication is required.\n By default, request will fail if the username cannot be extracted.\n If set to false, request will process and this provider will abstain.", + "key": "optional", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#optional(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Whether to propagate identity.", + "key": "propagate", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#propagate(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Configure outbound target for identity propagation.", + "key": "outbound", + "kind": "LIST", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#addOutboundTarget(io.helidon.security.providers.common.OutboundTarget)", + "type": "io.helidon.security.providers.common.OutboundTarget" + }, + { + "description": "Token handler to create outbound headers to propagate identity.\n If not defined, #atnTokenHandler will be used.", + "key": "outbound-token", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#outboundTokenHandler(io.helidon.security.util.TokenHandler)", + "type": "io.helidon.security.util.TokenHandler" + }, + { + "defaultValue": "USER", + "description": "Principal type this provider extracts (and also propagates).", + "key": "principal-type", + "method": "io.helidon.security.providers.header.HeaderAtnProvider.Builder#subjectType(io.helidon.security.SubjectType)", + "type": "io.helidon.security.SubjectType", + "allowedValues": [ + { + "description": "", + "value": "USER" + }, + { + "description": "", + "value": "SERVICE" + } + ] + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.openapi.ui", + "types": [ + { + "annotatedType": "io.helidon.openapi.ui.OpenApiUiConfig", + "type": "io.helidon.openapi.ui.OpenApiUi", + "producers": [ + "io.helidon.openapi.ui.OpenApiUiConfig#create(io.helidon.common.config.Config)", + "io.helidon.openapi.ui.OpenApiUiConfig#builder()", + "io.helidon.openapi.ui.OpenApiUi#create(io.helidon.openapi.ui.OpenApiUiConfig)" + ], + "options": [ + { + "description": "Full web context (not just the suffix).\n\n @return full web context path", + "key": "web-context", + "method": "io.helidon.openapi.ui.OpenApiUiConfig.Builder#webContext(java.lang.String)" + }, + { + "description": "Merges implementation-specific UI options.\n\n @return options for the UI to merge", + "key": "options", + "kind": "MAP", + "method": "io.helidon.openapi.ui.OpenApiUiConfig.Builder#options(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Sets whether the service should be enabled.\n\n @return `true` if enabled, `false` otherwise", + "key": "enabled", + "method": "io.helidon.openapi.ui.OpenApiUiConfig.Builder#isEnabled(java.lang.Boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.openapi", + "types": [ + { + "annotatedType": "io.helidon.openapi.OpenApiFeatureConfig", + "prefix": "openapi", + "type": "io.helidon.openapi.OpenApiFeature", + "standalone": true, + "producers": [ + "io.helidon.openapi.OpenApiFeatureConfig#create(io.helidon.common.config.Config)", + "io.helidon.openapi.OpenApiFeatureConfig#builder()", + "io.helidon.openapi.OpenApiFeature#create(io.helidon.openapi.OpenApiFeatureConfig)" + ], + "provides": [ + "io.helidon.webserver.spi.ServerFeatureProvider" + ], + "options": [ + { + "defaultValue": "/openapi", + "description": "Web context path for the OpenAPI endpoint.\n\n @return webContext to use", + "key": "web-context", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#webContext(java.lang.String)" + }, + { + "description": "CORS config.\n\n @return CORS config", + "key": "cors", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#cors(java.util.Optional)", + "type": "io.helidon.cors.CrossOriginConfig" + }, + { + "description": "List of sockets to register this feature on. If empty, it would get registered on all sockets.\n\n @return socket names to register on, defaults to empty (all available sockets)", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#sockets(java.util.Set)" + }, + { + "description": "OpenAPI manager.\n\n @return the OpenAPI manager", + "key": "manager", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#manager(java.util.Optional>)", + "providerType": "io.helidon.openapi.spi.OpenApiManagerProvider", + "type": "io.helidon.openapi.OpenApiManager", + "provider": true + }, + { + "defaultValue": "90.0", + "description": "Weight of the OpenAPI feature. This is quite low, to be registered after routing.\n {@value io.helidon.openapi.OpenApiFeature#WEIGHT}.\n\n @return weight of the feature", + "key": "weight", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#weight(double)", + "type": "java.lang.Double" + }, + { + "defaultValue": "openapi", + "description": "Hints for role names the user is expected to be in.\n\n @return list of hints", + "key": "roles", + "kind": "LIST", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#roles(java.util.List)" + }, + { + "description": "Path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`.\n\n @return location of the static OpenAPI document file", + "key": "static-file", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#staticFile(java.util.Optional)" + }, + { + "description": "OpenAPI services.\n\n @return the OpenAPI services", + "key": "services", + "kind": "LIST", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#services(java.util.List)", + "providerType": "io.helidon.openapi.spi.OpenApiServiceProvider", + "type": "io.helidon.openapi.OpenApiService", + "provider": true + }, + { + "defaultValue": "true", + "description": "Whether to allow anybody to access the endpoint.\n\n @return whether to permit access to metrics endpoint to anybody, defaults to `true`\n @see #roles()", + "key": "permit-all", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#permitAll(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Sets whether the feature should be enabled.\n\n @return `true` if enabled, `false` otherwise", + "key": "enabled", + "method": "io.helidon.openapi.OpenApiFeatureConfig.Builder#isEnabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.integrations.neo4j", + "types": [ + { + "annotatedType": "io.helidon.integrations.neo4j.Neo4j.Builder", + "type": "io.helidon.integrations.neo4j.Neo4j", + "producers": [ + "io.helidon.integrations.neo4j.Neo4j.Builder#build()", + "io.helidon.integrations.neo4j.Neo4j#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Set trust strategy.", + "key": "trust-strategy", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#trustStrategy(io.helidon.integrations.neo4j.Neo4j.Builder.TrustStrategy)", + "type": "io.helidon.integrations.neo4j.Neo4j.Builder.TrustStrategy", + "allowedValues": [ + { + "description": "Trust all.", + "value": "TRUST_ALL_CERTIFICATES" + }, + { + "description": "Trust custom certificates.", + "value": "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES" + }, + { + "description": "Trust system CA.", + "value": "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES" + } + ] + }, + { + "defaultValue": "PT1MS", + "description": "Set idle time.", + "key": "idle-time-before-connection-test", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#idleTimeBeforeConnectionTest(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "Set certificate path.", + "key": "certificate", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#certificate(java.nio.file.Path)", + "type": "java.nio.file.Path" + }, + { + "description": "Create uri.", + "key": "uri", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#uri(java.lang.String)" + }, + { + "description": "Enable metrics.", + "key": "metrics-enabled", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#metricsEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Enable hostname verification.", + "key": "hostname-verification-enabled", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#hostnameVerificationEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "PT1M", + "description": "Set connection acquisition timeout.", + "key": "connection-acquisition-timeout", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#connectionAcquisitionTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "100", + "description": "Set pool size.", + "key": "max-connection-pool-size", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#maxConnectionPoolSize(int)", + "type": "java.lang.Integer" + }, + { + "description": "Create password.", + "key": "password", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#password(java.lang.String)" + }, + { + "description": "Enable encrypted field.", + "key": "encrypted", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#encrypted(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Enable authentication.", + "key": "authentication-enabled", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#authenticationEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Enable log leaked sessions.", + "key": "log-leaked-sessions", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#logLeakedSessions(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Create username.", + "key": "username", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#username(java.lang.String)" + }, + { + "defaultValue": "PT5H", + "description": "Set max life time.", + "key": "max-connection-lifetime", + "method": "io.helidon.integrations.neo4j.Neo4j.Builder#maxConnectionLifetime(java.time.Duration)", + "type": "java.time.Duration" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.integrations.openapi.ui", + "types": [ + { + "annotatedType": "io.helidon.integrations.openapi.ui.OpenApiUiConfig", + "type": "io.helidon.integrations.openapi.ui.OpenApiUi", + "producers": [ + "io.helidon.integrations.openapi.ui.OpenApiUiConfig#create(io.helidon.common.config.Config)", + "io.helidon.integrations.openapi.ui.OpenApiUiConfig#builder()", + "io.helidon.integrations.openapi.ui.OpenApiUi#create(io.helidon.integrations.openapi.ui.OpenApiUiConfig)" + ], + "options": [ + { + "description": "Full web context (not just the suffix).\n\n @return full web context path", + "key": "web-context", + "method": "io.helidon.integrations.openapi.ui.OpenApiUiConfig.Builder#webContext(java.util.Optional)" + }, + { + "description": "Merges implementation-specific UI options.\n\n @return options for the UI to merge", + "key": "options", + "kind": "MAP", + "method": "io.helidon.integrations.openapi.ui.OpenApiUiConfig.Builder#options(java.util.Map)" + }, + { + "defaultValue": "true", + "description": "Sets whether the service should be enabled.\n\n @return `true` if enabled, `false` otherwise", + "key": "enabled", + "method": "io.helidon.integrations.openapi.ui.OpenApiUiConfig.Builder#isEnabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.integrations.micrometer", + "types": [ + { + "annotatedType": "io.helidon.integrations.micrometer.MicrometerFeature.Builder", + "prefix": "micrometer", + "type": "io.helidon.integrations.micrometer.MicrometerFeature", + "inherits": [ + "io.helidon.webserver.servicecommon.HelidonFeatureSupport.Builder" + ], + "producers": [ + "io.helidon.integrations.micrometer.MicrometerFeature.Builder#build()", + "io.helidon.integrations.micrometer.MicrometerFeature#create(io.helidon.common.config.Config)" + ], + "options": [] + } + ] + } +] +[ + { + "module": "io.helidon.integrations.oci.metrics", + "types": [ + { + "annotatedType": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder", + "type": "io.helidon.integrations.oci.metrics.OciMetricsSupport", + "producers": [ + "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#build()" + ], + "options": [ + { + "defaultValue": "60", + "description": "Sets the delay interval between metric posting\n (defaults to {@value #DEFAULT_SCHEDULER_DELAY}).", + "key": "delay", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#delay(long)", + "type": "java.lang.Long" + }, + { + "description": "Sets the resource group.", + "key": "resource-group", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#resourceGroup(java.lang.String)" + }, + { + "description": "Sets the compartment ID.", + "key": "compartment-id", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#compartmentId(java.lang.String)" + }, + { + "defaultValue": "1", + "description": "Sets the delay interval if metrics are posted in batches\n (defaults to {@value #DEFAULT_BATCH_DELAY}).", + "key": "batch-delay", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#batchDelay(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "50", + "description": "Sets the maximum no. of metrics to send in a batch\n (defaults to {@value #DEFAULT_BATCH_SIZE}).", + "key": "batch-size", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#batchSize(int)", + "type": "java.lang.Integer" + }, + { + "description": "Sets the namespace.", + "key": "namespace", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#namespace(java.lang.String)" + }, + { + "defaultValue": "All scopes", + "description": "Sets which metrics scopes (e.g., base, vendor, application) should be sent to OCI.\n

    \n If this method is never invoked, defaults to all scopes.\n

    ", + "key": "scopes", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#scopes(java.lang.String[])", + "type": "java.lang.String[]" + }, + { + "defaultValue": "TimeUnit.SECONDS", + "description": "Sets the time unit applied to the initial delay and delay values (defaults to `TimeUnit.SECONDS`).", + "key": "scheduling-time-unit", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#schedulingTimeUnit(java.util.concurrent.TimeUnit)", + "type": "java.util.concurrent.TimeUnit", + "allowedValues": [ + { + "description": "", + "value": "NANOSECONDS" + }, + { + "description": "", + "value": "MICROSECONDS" + }, + { + "description": "", + "value": "MILLISECONDS" + }, + { + "description": "", + "value": "SECONDS" + }, + { + "description": "", + "value": "MINUTES" + }, + { + "description": "", + "value": "HOURS" + }, + { + "description": "", + "value": "DAYS" + } + ] + }, + { + "defaultValue": "true", + "description": "Sets whether the description should be enabled or not.\n

    \n Defaults to `true`.\n

    ", + "key": "description-enabled", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#descriptionEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "1", + "description": "Sets the initial delay before metrics are sent to OCI\n (defaults to {@value #DEFAULT_SCHEDULER_INITIAL_DELAY}).", + "key": "initial-delay", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#initialDelay(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "true", + "description": "Sets whether metrics transmission to OCI is enabled.\n

    \n Defaults to `true`.\n

    ", + "key": "enabled", + "method": "io.helidon.integrations.oci.metrics.OciMetricsSupport.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.integrations.oci.sdk.runtime", + "types": [ + { + "annotatedType": "io.helidon.integrations.oci.sdk.runtime.OciConfig", + "prefix": "oci", + "type": "io.helidon.integrations.oci.sdk.runtime.OciConfig", + "standalone": true, + "producers": [ + "io.helidon.integrations.oci.sdk.runtime.OciConfig#create(io.helidon.common.config.Config)", + "io.helidon.integrations.oci.sdk.runtime.OciConfig#builder()" + ], + "options": [ + { + "description": "The OCI region.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, either this property or com.oracle.bmc.auth.RegionProvider must be provide a value in order\n to set the {@linkplain com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider#getRegion()}.\n\n @return the OCI region", + "key": "auth.region", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authRegion(java.lang.String)" + }, + { + "description": "The OCI tenant id.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, this property must be provided in order to set the\n {@linkplain com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider#getTenantId()}.\n\n @return the OCI tenant id", + "key": "auth.tenant-id", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authTenantId(java.lang.String)" + }, + { + "description": "The OCI configuration profile path.\n

    \n This configuration property has an effect only when `config-file` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #fileConfigIsPresent().\n When it is present, this property must also be present and then the\n {@linkplain com.oracle.bmc.ConfigFileReader#parse(String)}\n method will be passed this value. It is expected to be passed with a\n valid OCI configuration file path.\n\n @return the OCI configuration profile path", + "key": "config.path", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#configPath(java.lang.String)" + }, + { + "description": "The list of authentication strategies that will be attempted by\n com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider when one is\n called for. This is only used if #authStrategy() is not present.\n\n

      \n
    • `auto` - if present in the list, or if no value\n for this property exists.
    • \n
    • `config` - the\n com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider\n will be used, customized with other configuration\n properties described here.
    • \n
    • `config-file` - the\n com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider\n will be used, customized with other configuration\n properties described here.
    • \n
    • `instance-principals` - the\n com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider\n will be used.
    • \n
    • `resource-principal` - the\n com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider\n will be used.
    • \n
    \n

    \n If there are more than one strategy descriptors defined, the\n first one that is deemed to be available/suitable will be used and all others will be ignored.\n\n @return the list of authentication strategies that will be applied, defaulting to `auto`\n @see io.helidon.integrations.oci.sdk.runtime.OciAuthenticationDetailsProvider.AuthStrategy", + "key": "auth-strategies", + "kind": "LIST", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authStrategies(java.lang.String)", + "allowedValues": [ + { + "description": "auto select first applicable", + "value": "auto" + }, + { + "description": "simple authentication provider", + "value": "config" + }, + { + "description": "config file authentication provider", + "value": "config-file" + }, + { + "description": "instance principals authentication provider", + "value": "instance-principals" + }, + { + "description": "resource principal authentication provider", + "value": "resource-principal" + } + ] + }, + { + "defaultValue": "DEFAULT", + "description": "The OCI configuration/auth profile name.\n

    \n This configuration property has an effect only when `config-file` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #fileConfigIsPresent().\n When it is present, this property may also be optionally provided in order to override the default\n {@value #DEFAULT_PROFILE_NAME}.\n\n @return the optional OCI configuration/auth profile name", + "key": "config.profile", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#configProfile(java.lang.String)" + }, + { + "defaultValue": "oci_api_key.pem", + "description": "The OCI authentication key file.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, this property must be provided in order to set the\n {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPrivateKey()}. This file must exist in the\n `user.home` directory. Alternatively, this property can be set using either #authPrivateKey() or\n using #authPrivateKeyPath().\n\n @return the OCI authentication key file", + "key": "auth.keyFile", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authKeyFile(java.lang.String)" + }, + { + "description": "The OCI authentication private key.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, this property must be provided in order to set the\n {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPrivateKey()}. Alternatively, this property\n can be set using either #authKeyFile() residing in the `user.home` directory, or using\n #authPrivateKeyPath().\n\n @return the OCI authentication private key", + "key": "auth.private-key", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authPrivateKey(char[])", + "type": "char[]" + }, + { + "description": "The OCI user id.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies().\n When it is present, this property must be provided in order to set the\n {@linkplain com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider#getUserId()}.\n\n @return the OCI user id", + "key": "auth.user-id", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authUserId(java.lang.String)" + }, + { + "defaultValue": "169.254.169.254", + "description": "The OCI IMDS hostname.\n

    \n This configuration property is used to identify the metadata service url.\n\n @return the OCI IMDS hostname", + "key": "imds.hostname", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#imdsHostName(java.lang.String)" + }, + { + "description": "The OCI authentication fingerprint.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, this property must be provided in order to set the API signing key's fingerprint.\n See {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getFingerprint()} for more details.\n\n @return the OCI authentication fingerprint", + "key": "auth.fingerprint", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authFingerprint(java.lang.String)" + }, + { + "description": "The singular authentication strategy to apply. This will be preferred over #authStrategies() if both are\n present.\n\n @return the singular authentication strategy to be applied", + "key": "auth-strategy", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authStrategy(java.lang.String)", + "allowedValues": [ + { + "description": "auto select first applicable", + "value": "auto" + }, + { + "description": "simple authentication provider", + "value": "config" + }, + { + "description": "config file authentication provider", + "value": "config-file" + }, + { + "description": "instance principals authentication provider", + "value": "instance-principals" + }, + { + "description": "resource principals authentication provider", + "value": "resource-principal" + } + ] + }, + { + "description": "The OCI authentication key file path.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, this property must be provided in order to set the\n {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPrivateKey()}. This file path is\n an alternative for using #authKeyFile() where the file must exist in the `user.home` directory.\n Alternatively, this property can be set using #authPrivateKey().\n\n @return the OCI authentication key file path", + "key": "auth.private-key-path", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authPrivateKeyPath(java.lang.String)" + }, + { + "description": "The OCI authentication passphrase.\n

    \n This configuration property has an effect only when `config` is, explicitly or implicitly,\n present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent().\n When it is present, this property must be provided in order to set the\n {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPassphraseCharacters()}.\n\n @return the OCI authentication passphrase", + "key": "auth.passphrase", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#authPassphrase(char[])", + "type": "char[]" + }, + { + "defaultValue": "PT0.1S", + "description": "The OCI IMDS connection timeout. This is used to auto-detect availability.\n

    \n This configuration property is used when attempting to connect to the metadata service.\n\n @return the OCI IMDS connection timeout\n @see OciAvailability", + "key": "imds.timeout.milliseconds", + "method": "io.helidon.integrations.oci.sdk.runtime.OciConfig.Builder#imdsTimeout(java.time.Duration)", + "type": "java.time.Duration" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.websocket", + "types": [ + { + "annotatedType": "io.helidon.webserver.websocket.WsConfig", + "type": "io.helidon.webserver.websocket.WsConfig", + "producers": [ + "io.helidon.webserver.websocket.WsConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.websocket.WsConfig#builder()" + ], + "provides": [ + "io.helidon.webserver.spi.ProtocolConfig" + ], + "options": [ + { + "description": "WebSocket origins.\n\n @return origins", + "key": "origins", + "kind": "LIST", + "method": "io.helidon.webserver.websocket.WsConfig.Builder#origins(java.util.Set)" + }, + { + "defaultValue": "websocket", + "description": "Name of this configuration.\n\n @return configuration name", + "key": "name", + "method": "io.helidon.webserver.websocket.WsConfig.Builder#name(java.lang.String)" + }, + { + "defaultValue": "1048576", + "description": "Max WebSocket frame size supported by the server on a read operation.\n Default is 1 MB.\n\n @return max frame size to read", + "key": "max-frame-length", + "method": "io.helidon.webserver.websocket.WsConfig.Builder#maxFrameLength(int)", + "type": "java.lang.Integer" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.context", + "types": [ + { + "annotatedType": "io.helidon.webserver.context.ContextFeatureConfig", + "prefix": "context", + "type": "io.helidon.webserver.context.ContextFeature", + "producers": [ + "io.helidon.webserver.context.ContextFeatureConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.context.ContextFeatureConfig#builder()", + "io.helidon.webserver.context.ContextFeature#create(io.helidon.webserver.context.ContextFeatureConfig)" + ], + "provides": [ + "io.helidon.webserver.spi.ServerFeatureProvider" + ], + "options": [ + { + "description": "List of sockets to register this feature on. If empty, it would get registered on all sockets.\n\n @return socket names to register on, defaults to empty (all available sockets)", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.webserver.context.ContextFeatureConfig.Builder#sockets(java.util.Set)" + }, + { + "defaultValue": "1100.0", + "description": "Weight of the context feature. As it is used by other features, the default is quite high:\n {@value io.helidon.webserver.context.ContextFeature#WEIGHT}.\n\n @return weight of the feature", + "key": "weight", + "method": "io.helidon.webserver.context.ContextFeatureConfig.Builder#weight(double)", + "type": "java.lang.Double" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.accesslog", + "types": [ + { + "annotatedType": "io.helidon.webserver.accesslog.AccessLogConfig", + "prefix": "access-log", + "type": "io.helidon.webserver.accesslog.AccessLogFeature", + "producers": [ + "io.helidon.webserver.accesslog.AccessLogConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.accesslog.AccessLogConfig#builder()", + "io.helidon.webserver.accesslog.AccessLogFeature#create(io.helidon.webserver.accesslog.AccessLogConfig)" + ], + "provides": [ + "io.helidon.webserver.spi.ServerFeatureProvider" + ], + "options": [ + { + "description": "List of sockets to register this feature on. If empty, it would get registered on all sockets.\n The logger used will have the expected logger with a suffix of the socket name.\n\n @return socket names to register on, defaults to empty (all available sockets)", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.webserver.accesslog.AccessLogConfig.Builder#sockets(java.util.Set)" + }, + { + "defaultValue": "1000.0", + "description": "Weight of the access log feature. We need to log access for anything happening on the server, so weight is high:\n {@value io.helidon.webserver.accesslog.AccessLogFeature#WEIGHT}.\n\n @return weight of the feature", + "key": "weight", + "method": "io.helidon.webserver.accesslog.AccessLogConfig.Builder#weight(double)", + "type": "java.lang.Double" + }, + { + "defaultValue": "io.helidon.webserver.AccessLog", + "description": "Name of the logger used to obtain access log logger from System#getLogger(String).\n Defaults to {@value AccessLogFeature#DEFAULT_LOGGER_NAME}.\n\n @return name of the logger to use", + "key": "logger-name", + "method": "io.helidon.webserver.accesslog.AccessLogConfig.Builder#loggerName(java.lang.String)" + }, + { + "description": "The format for log entries (similar to the Apache `LogFormat`).\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    Log format elements
    %hIP address of the remote hostHostLogEntry
    %lThe client identity. This is always undefined in Helidon.UserIdLogEntry
    %uUser ID as asserted by Helidon Security.UserLogEntry
    %tThe timestampTimestampLogEntry
    %rThe request line (`\"GET /favicon.ico HTTP/1.0\"`)RequestLineLogEntry
    %sThe status code returned to the clientStatusLogEntry
    %bThe entity size in bytesSizeLogEntry
    %DThe time taken in microseconds (start of request until last byte written)TimeTakenLogEntry
    %TThe time taken in seconds (start of request until last byte written), integerTimeTakenLogEntry
    %{header-name}iValue of header `header-name`HeaderLogEntry
    \n\n @return format string, such as `%h %l %u %t %r %b %{Referer`i}", + "key": "format", + "method": "io.helidon.webserver.accesslog.AccessLogConfig.Builder#format(java.util.Optional)" + }, + { + "defaultValue": "true", + "description": "Whether this feature will be enabled.\n\n @return whether enabled", + "key": "enabled", + "method": "io.helidon.webserver.accesslog.AccessLogConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.grpc", + "types": [ + { + "annotatedType": "io.helidon.webserver.grpc.GrpcConfig", + "type": "io.helidon.webserver.grpc.GrpcConfig", + "producers": [ + "io.helidon.webserver.grpc.GrpcConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.grpc.GrpcConfig#builder()" + ], + "provides": [ + "io.helidon.webserver.spi.ProtocolConfig" + ], + "options": [] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.security", + "types": [ + { + "annotatedType": "io.helidon.webserver.security.SecurityHandlerConfig", + "type": "io.helidon.webserver.security.SecurityHandler", + "producers": [ + "io.helidon.webserver.security.SecurityHandlerConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.security.SecurityHandlerConfig#builder()", + "io.helidon.webserver.security.SecurityHandler#create(io.helidon.webserver.security.SecurityHandlerConfig)" + ], + "options": [ + { + "description": "If called, authentication failure will not abort request and will continue as anonymous (defaults to false).\n\n @return whether authn is optional", + "key": "authentication-optional", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#authenticationOptional(java.util.Optional)", + "type": "java.lang.Boolean" + }, + { + "description": "An array of allowed roles for this path - must have a security provider supporting roles (either authentication\n or authorization provider).\n This method enables authentication and authorization (you can disable them again by calling\n SecurityHandler#skipAuthorization()\n and #authenticationOptional() if needed).\n\n @return if subject is any of these roles, allow access", + "key": "roles-allowed", + "kind": "LIST", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#rolesAllowed(java.util.Set)" + }, + { + "description": "If called, request will go through authentication process - defaults to false (even if authorize is true).\n\n @return whether to authenticate or not", + "key": "authenticate", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#authenticate(java.util.Optional)", + "type": "java.lang.Boolean" + }, + { + "description": "Use a named authorizer (as supported by security - if not defined, default authorizer is used, if none defined, all is\n permitted).\n Will enable authorization.\n\n @return name of authorizer as configured in io.helidon.security.Security", + "key": "authorizer", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#authorizer(java.util.Optional)" + }, + { + "description": "List of sockets this configuration should be applied to.\n If empty, the configuration is applied to all configured sockets.\n\n @return list of sockets", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#sockets(java.util.List)" + }, + { + "description": "Override for event-type, defaults to {@value SecurityHandler#DEFAULT_AUDIT_EVENT_TYPE}.\n\n @return audit event type to use", + "key": "audit-event-type", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#auditEventType(java.util.Optional)" + }, + { + "description": "Whether to audit this request - defaults to false, if enabled, request is audited with event type \"request\".\n\n @return whether to audit", + "key": "audit", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#audit(java.util.Optional)", + "type": "java.lang.Boolean" + }, + { + "description": "Override for audit message format, defaults to {@value SecurityHandler#DEFAULT_AUDIT_MESSAGE_FORMAT}.\n\n @return audit message format to use", + "key": "audit-message-format", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#auditMessageFormat(java.util.Optional)" + }, + { + "description": "Use a named authenticator (as supported by security - if not defined, default authenticator is used).\n Will enable authentication.\n\n @return name of authenticator as configured in io.helidon.security.Security", + "key": "authenticator", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#authenticator(java.util.Optional)" + }, + { + "description": "Enable authorization for this route.\n\n @return whether to authorize", + "key": "authorize", + "method": "io.helidon.webserver.security.SecurityHandlerConfig.Builder#authorize(java.util.Optional)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.security.PathsConfig", + "type": "io.helidon.webserver.security.PathsConfig", + "producers": [ + "io.helidon.webserver.security.PathsConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.security.PathsConfig#builder()" + ], + "options": [ + { + "description": "", + "key": "path", + "method": "io.helidon.webserver.security.PathsConfig.Builder#path(java.lang.String)" + }, + { + "description": "", + "key": "handler", + "method": "io.helidon.webserver.security.PathsConfig.Builder#handler(io.helidon.webserver.security.SecurityHandler)", + "type": "io.helidon.webserver.security.SecurityHandler", + "merge": true + }, + { + "defaultValue": "@default", + "description": "", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.webserver.security.PathsConfig.Builder#sockets(java.util.List)" + }, + { + "description": "", + "key": "methods", + "kind": "LIST", + "method": "io.helidon.webserver.security.PathsConfig.Builder#methods(java.util.List)", + "type": "io.helidon.http.Method" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.security.SecurityFeatureConfig", + "prefix": "security", + "type": "io.helidon.webserver.security.SecurityFeature", + "producers": [ + "io.helidon.webserver.security.SecurityFeatureConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.security.SecurityFeatureConfig#builder()", + "io.helidon.webserver.security.SecurityFeature#create(io.helidon.webserver.security.SecurityFeatureConfig)" + ], + "provides": [ + "io.helidon.webserver.spi.ServerFeatureProvider" + ], + "options": [ + { + "description": "Configuration for webserver paths.\n\n @return path configuration", + "key": "paths", + "kind": "LIST", + "method": "io.helidon.webserver.security.SecurityFeatureConfig.Builder#paths(java.util.List)", + "type": "io.helidon.webserver.security.PathsConfig" + }, + { + "defaultValue": "800.0", + "description": "Weight of the security feature. Value is:\n {@value io.helidon.webserver.security.SecurityFeature#WEIGHT}.\n\n @return weight of the feature", + "key": "weight", + "method": "io.helidon.webserver.security.SecurityFeatureConfig.Builder#weight(double)", + "type": "java.lang.Double" + }, + { + "defaultValue": "SecurityHandler.create()", + "description": "The default security handler.\n\n @return security handler defaults", + "key": "defaults", + "method": "io.helidon.webserver.security.SecurityFeatureConfig.Builder#defaults(io.helidon.webserver.security.SecurityHandler)", + "type": "io.helidon.webserver.security.SecurityHandler" + }, + { + "description": "Security associated with this feature.\n If not specified here, the feature uses security registered with\n io.helidon.common.context.Contexts#globalContext(), if not found, it creates a new\n instance from root of configuration (using `security` key).\n

    \n This configuration allows usage of a different security instance for a specific security feature setup.\n\n @return security instance to be used to handle security in this feature configuration", + "key": "security", + "method": "io.helidon.webserver.security.SecurityFeatureConfig.Builder#security(io.helidon.security.Security)", + "type": "io.helidon.security.Security" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe.metrics", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.metrics.MetricsObserverConfig", + "prefix": "metrics", + "type": "io.helidon.webserver.observe.metrics.MetricsObserver", + "standalone": true, + "producers": [ + "io.helidon.webserver.observe.metrics.MetricsObserverConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.metrics.MetricsObserverConfig#builder()", + "io.helidon.webserver.observe.metrics.MetricsObserver#create(io.helidon.webserver.observe.metrics.MetricsObserverConfig)" + ], + "provides": [ + "io.helidon.webserver.observe.spi.ObserveProvider" + ], + "options": [ + { + "defaultValue": "metrics", + "description": "", + "key": "endpoint", + "method": "io.helidon.webserver.observe.metrics.MetricsObserverConfig.Builder#endpoint(java.lang.String)" + }, + { + "defaultValue": "@io.helidon.metrics.api.MetricsConfig@.create()", + "description": "Assigns `MetricsSettings` which will be used in creating the `MetricsSupport` instance at build-time.\n\n @return the metrics settings to assign for use in building the `MetricsSupport` instance", + "key": "metrics-config", + "method": "io.helidon.webserver.observe.metrics.MetricsObserverConfig.Builder#metricsConfig(io.helidon.metrics.api.MetricsConfig)", + "type": "io.helidon.metrics.api.MetricsConfig", + "merge": true + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe.tracing", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.tracing.TracingObserverConfig", + "type": "io.helidon.webserver.observe.tracing.TracingObserver", + "producers": [ + "io.helidon.webserver.observe.tracing.TracingObserverConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.tracing.TracingObserverConfig#builder()", + "io.helidon.webserver.observe.tracing.TracingObserver#create(io.helidon.webserver.observe.tracing.TracingObserverConfig)" + ], + "provides": [ + "io.helidon.webserver.observe.spi.ObserveProvider" + ], + "options": [ + { + "defaultValue": "TracingConfig.ENABLED", + "description": "Use the provided configuration as a default for any request.\n\n @return default web server tracing configuration", + "key": "env-config", + "method": "io.helidon.webserver.observe.tracing.TracingObserverConfig.Builder#envConfig(io.helidon.tracing.config.TracingConfig)", + "type": "io.helidon.tracing.config.TracingConfig", + "merge": true + }, + { + "defaultValue": "new @java.util.ArrayList@(@java.util.List@.of(PathTracingConfig.builder()\n .path(\"/metrics/*\")\n .tracingConfig(TracingConfig.DISABLED)\n .build(), \n PathTracingConfig.builder()\n .path(\"/observe/metrics/*\")\n .tracingConfig(TracingConfig.DISABLED)\n .build(), \n PathTracingConfig.builder()\n .path(\"/health/*\")\n .tracingConfig(TracingConfig.DISABLED)\n .build(), \n PathTracingConfig.builder()\n .path(\"/observe/health/*\")\n .tracingConfig(TracingConfig.DISABLED)\n .build(), \n PathTracingConfig.builder()\n .path(\"/openapi/*\")\n .tracingConfig(TracingConfig.DISABLED)\n .build(), \n PathTracingConfig.builder()\n .path(\"/observe/openapi/*\")\n .tracingConfig(TracingConfig.DISABLED)\n .build()))", + "description": "Path specific configuration of tracing.\n\n @return configuration of tracing for specific paths", + "key": "paths", + "kind": "LIST", + "method": "io.helidon.webserver.observe.tracing.TracingObserverConfig.Builder#pathConfigs(java.util.List)", + "type": "io.helidon.webserver.observe.tracing.PathTracingConfig" + }, + { + "defaultValue": "900.0", + "description": "Weight of the feature registered with WebServer.\n Changing weight may cause tracing to be executed at a different time (such as after security, or even after\n all routes). Please understand feature weights before changing this order.\n\n @return weight of tracing feature", + "key": "weight", + "method": "io.helidon.webserver.observe.tracing.TracingObserverConfig.Builder#weight(double)", + "type": "java.lang.Double" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe.config", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.config.ConfigObserverConfig", + "type": "io.helidon.webserver.observe.config.ConfigObserver", + "producers": [ + "io.helidon.webserver.observe.config.ConfigObserverConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.config.ConfigObserverConfig#builder()", + "io.helidon.webserver.observe.config.ConfigObserver#create(io.helidon.webserver.observe.config.ConfigObserverConfig)" + ], + "provides": [ + "io.helidon.webserver.observe.spi.ObserveProvider" + ], + "options": [ + { + "defaultValue": "config", + "description": "", + "key": "endpoint", + "method": "io.helidon.webserver.observe.config.ConfigObserverConfig.Builder#endpoint(java.lang.String)" + }, + { + "defaultValue": ".*password, .*passphrase, .*secret", + "description": "Secret patterns (regular expressions) to exclude from output.\n Any pattern that matches a key will cause the output to be obfuscated and not contain the value.\n

    \n Patterns always added:\n

      \n
    • `.*password`
    • \n
    • `.*passphrase`
    • \n
    • `.*secret`
    • \n
    \n\n @return set of regular expression patterns for keys, where values should be excluded from output", + "key": "secrets", + "kind": "LIST", + "method": "io.helidon.webserver.observe.config.ConfigObserverConfig.Builder#secrets(java.util.Set)" + }, + { + "description": "Permit all access, even when not authorized.\n\n @return whether to permit access for anybody", + "key": "permit-all", + "method": "io.helidon.webserver.observe.config.ConfigObserverConfig.Builder#permitAll(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe.health", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.health.HealthObserverConfig", + "prefix": "health", + "type": "io.helidon.webserver.observe.health.HealthObserver", + "standalone": true, + "producers": [ + "io.helidon.webserver.observe.health.HealthObserverConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.health.HealthObserverConfig#builder()", + "io.helidon.webserver.observe.health.HealthObserver#create(io.helidon.webserver.observe.health.HealthObserverConfig)" + ], + "provides": [ + "io.helidon.webserver.observe.spi.ObserveProvider" + ], + "options": [ + { + "defaultValue": "health", + "description": "", + "key": "endpoint", + "method": "io.helidon.webserver.observe.health.HealthObserverConfig.Builder#endpoint(java.lang.String)" + }, + { + "defaultValue": "false", + "description": "Whether details should be printed.\n By default, health only returns a io.helidon.http.Status#NO_CONTENT_204 for success,\n io.helidon.http.Status#SERVICE_UNAVAILABLE_503 for health down,\n and io.helidon.http.Status#INTERNAL_SERVER_ERROR_500 in case of error with no entity.\n When details are enabled, health returns io.helidon.http.Status#OK_200 for success, same codes\n otherwise\n and a JSON entity with detailed information about each health check executed.\n\n @return set to `true` to enable details", + "key": "details", + "method": "io.helidon.webserver.observe.health.HealthObserverConfig.Builder#details(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Whether to use services discovered by java.util.ServiceLoader.\n By default, all io.helidon.health.spi.HealthCheckProvider based health checks are added.\n\n @return set to `false` to disable discovery", + "key": "use-system-services", + "method": "io.helidon.webserver.observe.health.HealthObserverConfig.Builder#useSystemServices(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.ObserverConfigBase", + "type": "io.helidon.webserver.observe.ObserverConfigBase", + "producers": [ + "io.helidon.webserver.observe.ObserverConfigBase#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.ObserverConfigBase#builder()" + ], + "options": [ + { + "defaultValue": "true", + "description": "Whether this observer is enabled.\n\n @return `false` to disable observer", + "key": "enabled", + "method": "io.helidon.webserver.observe.ObserverConfigBase.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.observe.ObserveFeatureConfig", + "prefix": "observe", + "type": "io.helidon.webserver.observe.ObserveFeature", + "producers": [ + "io.helidon.webserver.observe.ObserveFeatureConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.ObserveFeatureConfig#builder()", + "io.helidon.webserver.observe.ObserveFeature#create(io.helidon.webserver.observe.ObserveFeatureConfig)" + ], + "provides": [ + "io.helidon.webserver.spi.ServerFeatureProvider" + ], + "options": [ + { + "defaultValue": "@io.helidon.cors.CrossOriginConfig@.create()", + "description": "Cors support inherited by each observe provider, unless explicitly configured.\n\n @return cors support to use", + "key": "cors", + "method": "io.helidon.webserver.observe.ObserveFeatureConfig.Builder#cors(io.helidon.cors.CrossOriginConfig)", + "type": "io.helidon.cors.CrossOriginConfig" + }, + { + "defaultValue": "/observe", + "description": "Root endpoint to use for observe providers. By default, all observe endpoint are under this root endpoint.\n

    \n Example:\n
    \n If root endpoint is `/observe` (the default), and default health endpoint is `health` (relative),\n health endpoint would be `/observe/health`.\n\n @return endpoint to use", + "key": "endpoint", + "method": "io.helidon.webserver.observe.ObserveFeatureConfig.Builder#endpoint(java.lang.String)" + }, + { + "description": "Sockets the observability endpoint should be exposed on. If not defined, defaults to the default socket\n ({@value io.helidon.webserver.WebServer#DEFAULT_SOCKET_NAME}.\n Each observer may have its own configuration of sockets that are relevant to it, this only controls the endpoints!\n\n @return list of sockets to register observe endpoint on", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.webserver.observe.ObserveFeatureConfig.Builder#sockets(java.util.List)" + }, + { + "defaultValue": "80.0", + "description": "Change the weight of this feature. This may change the order of registration of this feature.\n By default, observability weight is {@value ObserveFeature#WEIGHT} so it is registered after routing.\n\n @return weight to use", + "key": "weight", + "method": "io.helidon.webserver.observe.ObserveFeatureConfig.Builder#weight(double)", + "type": "java.lang.Double" + }, + { + "defaultValue": "true", + "description": "Whether the observe support is enabled.\n\n @return `false` to disable observe feature", + "key": "enabled", + "method": "io.helidon.webserver.observe.ObserveFeatureConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Observers to use with this observe features.\n Each observer type is registered only once, unless it uses a custom name (default name is the same as the type).\n\n @return list of observers to use in this feature", + "key": "observers", + "kind": "LIST", + "method": "io.helidon.webserver.observe.ObserveFeatureConfig.Builder#observers(java.util.List)", + "providerType": "io.helidon.webserver.observe.spi.ObserveProvider", + "type": "io.helidon.webserver.observe.spi.Observer", + "provider": true + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe.info", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.info.InfoObserverConfig", + "type": "io.helidon.webserver.observe.info.InfoObserver", + "producers": [ + "io.helidon.webserver.observe.info.InfoObserverConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.info.InfoObserverConfig#builder()", + "io.helidon.webserver.observe.info.InfoObserver#create(io.helidon.webserver.observe.info.InfoObserverConfig)" + ], + "provides": [ + "io.helidon.webserver.observe.spi.ObserveProvider" + ], + "options": [ + { + "defaultValue": "info", + "description": "", + "key": "endpoint", + "method": "io.helidon.webserver.observe.info.InfoObserverConfig.Builder#endpoint(java.lang.String)" + }, + { + "description": "Values to be exposed using this observability endpoint.\n\n @return value map", + "key": "values", + "kind": "MAP", + "method": "io.helidon.webserver.observe.info.InfoObserverConfig.Builder#values(java.util.Map)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.observe.log", + "types": [ + { + "annotatedType": "io.helidon.webserver.observe.log.LogStreamConfig", + "type": "io.helidon.webserver.observe.log.LogStreamConfig", + "producers": [ + "io.helidon.webserver.observe.log.LogStreamConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.log.LogStreamConfig#builder()" + ], + "options": [ + { + "defaultValue": "PT5S", + "description": "How long to wait before we send the idle message, to make sure we keep the stream alive.\n\n @return if no messages appear within this duration, and idle message will be sent\n @see #idleString()", + "key": "idle-message-timeout", + "method": "io.helidon.webserver.observe.log.LogStreamConfig.Builder#idleMessageTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "100", + "description": "Length of the in-memory queue that buffers log messages from loggers before sending them over the network.\n If the messages are produced faster than we can send them to client, excess messages are DISCARDED, and will not\n be sent.\n\n @return size of the in-memory queue for log messages", + "key": "queue-size", + "method": "io.helidon.webserver.observe.log.LogStreamConfig.Builder#queueSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "@io.helidon.http.HttpMediaTypes@.PLAINTEXT_UTF_8", + "description": "", + "key": "content-type", + "method": "io.helidon.webserver.observe.log.LogStreamConfig.Builder#contentType(io.helidon.http.HttpMediaType)", + "type": "io.helidon.http.HttpMediaType" + }, + { + "defaultValue": "%\n", + "description": "String sent when there are no log messages within the #idleMessageTimeout().\n\n @return string to write over the network when no log messages are received", + "key": "idle-string", + "method": "io.helidon.webserver.observe.log.LogStreamConfig.Builder#idleString(java.lang.String)" + }, + { + "defaultValue": "true", + "description": "Whether stream is enabled.\n\n @return whether to allow streaming of log statements", + "key": "enabled", + "method": "io.helidon.webserver.observe.log.LogStreamConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.observe.log.LogObserverConfig", + "type": "io.helidon.webserver.observe.log.LogObserver", + "producers": [ + "io.helidon.webserver.observe.log.LogObserverConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.observe.log.LogObserverConfig#builder()", + "io.helidon.webserver.observe.log.LogObserver#create(io.helidon.webserver.observe.log.LogObserverConfig)" + ], + "provides": [ + "io.helidon.webserver.observe.spi.ObserveProvider" + ], + "options": [ + { + "defaultValue": "log", + "description": "", + "key": "endpoint", + "method": "io.helidon.webserver.observe.log.LogObserverConfig.Builder#endpoint(java.lang.String)" + }, + { + "defaultValue": "@io.helidon.webserver.observe.log.LogStreamConfig@.create()", + "description": "Configuration of log stream.\n\n @return log stream configuration", + "key": "stream", + "method": "io.helidon.webserver.observe.log.LogObserverConfig.Builder#stream(io.helidon.webserver.observe.log.LogStreamConfig)", + "type": "io.helidon.webserver.observe.log.LogStreamConfig" + }, + { + "description": "Permit all access, even when not authorized.\n\n @return whether to permit access for anybody", + "key": "permit-all", + "method": "io.helidon.webserver.observe.log.LogObserverConfig.Builder#permitAll(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.http2", + "types": [ + { + "annotatedType": "io.helidon.webserver.http2.Http2Config", + "type": "io.helidon.webserver.http2.Http2Config", + "producers": [ + "io.helidon.webserver.http2.Http2Config#create(io.helidon.common.config.Config)", + "io.helidon.webserver.http2.Http2Config#builder()" + ], + "provides": [ + "io.helidon.webserver.spi.ProtocolConfig" + ], + "options": [ + { + "defaultValue": "8192", + "description": "Maximum number of concurrent streams that the server will allow.\n Defaults to `8192`. This limit is directional: it applies to the number of streams that the sender\n permits the receiver to create.\n It is recommended that this value be no smaller than 100 to not unnecessarily limit parallelism\n See RFC 9113 section 6.5.2 for details.\n\n @return maximal number of concurrent streams", + "key": "max-concurrent-streams", + "method": "io.helidon.webserver.http2.Http2Config.Builder#maxConcurrentStreams(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "PT10S", + "description": "Period for counting rapid resets(stream RST sent by client before any data have been sent by server).\n Default value is `PT10S`.\n\n @return duration\n @see CVE-2023-44487\n @see ISO_8601 Durations", + "key": "rapid-reset-check-period", + "method": "io.helidon.webserver.http2.Http2Config.Builder#rapidResetCheckPeriod(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "100", + "description": "Maximum number of rapid resets(stream RST sent by client before any data have been sent by server).\n When reached within #rapidResetCheckPeriod(), GOAWAY is sent to client and connection is closed.\n Default value is `100`.\n\n @return maximum number of rapid resets\n @see CVE-2023-44487", + "key": "max-rapid-resets", + "method": "io.helidon.webserver.http2.Http2Config.Builder#maxRapidResets(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "16384", + "description": "The size of the largest frame payload that the sender is willing to receive in bytes.\n Default value is `16384` and maximum value is 224-1 = 16777215 bytes.\n See RFC 9113 section 6.5.2 for details.\n\n @return maximal frame size", + "key": "max-frame-size", + "method": "io.helidon.webserver.http2.Http2Config.Builder#maxFrameSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "false", + "description": "Whether to send error message over HTTP to client.\n Defaults to `false`, as exception message may contain internal information that could be used as an\n attack vector. Use with care and in cases where both server and clients are under your full control (such as for\n testing).\n\n @return whether to send error messages over the network", + "key": "send-error-details", + "method": "io.helidon.webserver.http2.Http2Config.Builder#sendErrorDetails(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "8192", + "description": "The maximum field section size that the sender is prepared to accept in bytes.\n See RFC 9113 section 6.5.2 for details.\n Default is 8192.\n\n @return maximal header list size in bytes", + "key": "max-header-list-size", + "method": "io.helidon.webserver.http2.Http2Config.Builder#maxHeaderListSize(long)", + "type": "java.lang.Long" + }, + { + "description": "Requested URI discovery settings.\n\n @return settings for computing the requested URI", + "key": "requested-uri-discovery", + "method": "io.helidon.webserver.http2.Http2Config.Builder#requestedUriDiscovery(io.helidon.http.RequestedUriDiscoveryContext)", + "type": "io.helidon.http.RequestedUriDiscoveryContext" + }, + { + "defaultValue": "1048576", + "description": "This setting indicates the sender's maximum window size in bytes for stream-level flow control.\n Default and maximum value is 231-1 = 2147483647 bytes. This setting affects the window size\n of HTTP/2 connection.\n Any value greater than 2147483647 causes an error. Any value smaller than initial window size causes an error.\n See RFC 9113 section 6.9.1 for details.\n\n @return maximum window size in bytes", + "key": "initial-window-size", + "method": "io.helidon.webserver.http2.Http2Config.Builder#initialWindowSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "PT0.1S", + "description": "Outbound flow control blocking timeout configured as java.time.Duration\n or text in ISO-8601 format.\n Blocking timeout defines an interval to wait for the outbound window size changes(incoming window updates)\n before the next blocking iteration.\n Default value is `PT0.1S`.\n\n \n \n \n \n \n
    ISO_8601 format examples:
    PT0.1S100 milliseconds
    PT0.5S500 milliseconds
    PT2S2 seconds
    \n\n @return duration\n @see ISO_8601 Durations", + "key": "flow-control-timeout", + "method": "io.helidon.webserver.http2.Http2Config.Builder#flowControlTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "true", + "description": "If set to false, any path is accepted (even containing illegal characters).\n\n @return whether to validate path", + "key": "validate-path", + "method": "io.helidon.webserver.http2.Http2Config.Builder#validatePath(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "10", + "description": "Maximum number of consecutive empty frames allowed on connection.\n\n @return max number of consecutive empty frames", + "key": "max-empty-frames", + "method": "io.helidon.webserver.http2.Http2Config.Builder#maxEmptyFrames(int)", + "type": "java.lang.Integer" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver", + "types": [ + { + "annotatedType": "io.helidon.webserver.WebServerConfig", + "prefix": "server", + "type": "io.helidon.webserver.WebServer", + "standalone": true, + "inherits": [ + "io.helidon.webserver.ListenerConfig" + ], + "producers": [ + "io.helidon.webserver.WebServerConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.WebServerConfig#builder()", + "io.helidon.webserver.WebServer#create(io.helidon.webserver.WebServerConfig)" + ], + "options": [ + { + "description": "Server features allow customization of the server, listeners, or routings.\n\n @return server features", + "key": "features", + "kind": "LIST", + "method": "io.helidon.webserver.WebServerConfig.Builder#features(java.util.List)", + "providerType": "io.helidon.webserver.spi.ServerFeatureProvider", + "type": "io.helidon.webserver.spi.ServerFeature", + "provider": true + }, + { + "description": "Socket configurations.\n Note that socket named {@value WebServer#DEFAULT_SOCKET_NAME} cannot be used,\n configure the values on the server directly.\n\n @return map of listener configurations, except for the default one", + "key": "sockets", + "kind": "MAP", + "method": "io.helidon.webserver.WebServerConfig.Builder#sockets(java.util.Map)", + "type": "io.helidon.webserver.ListenerConfig" + }, + { + "defaultValue": "true", + "description": "When true the webserver registers a shutdown hook with the JVM Runtime.\n

    \n Defaults to true. Set this to false such that a shutdown hook is not registered.\n\n @return whether to register a shutdown hook", + "key": "shutdown-hook", + "method": "io.helidon.webserver.WebServerConfig.Builder#shutdownHook(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.ConnectionConfig", + "type": "io.helidon.webserver.ConnectionConfig", + "producers": [ + "io.helidon.webserver.ConnectionConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.ConnectionConfig#builder()" + ], + "options": [ + { + "defaultValue": "PT30S", + "description": "Read timeout.\n Default is {@value #DEFAULT_READ_TIMEOUT_DURATION}\n\n @return read timeout", + "key": "read-timeout", + "method": "io.helidon.webserver.ConnectionConfig.Builder#readTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "true", + "description": "Configure socket keep alive.\n Default is `true`.\n\n @return keep alive\n @see java.net.StandardSocketOptions#SO_KEEPALIVE", + "key": "keep-alive", + "method": "io.helidon.webserver.ConnectionConfig.Builder#keepAlive(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "PT10S", + "description": "Connect timeout.\n Default is {@value #DEFAULT_CONNECT_TIMEOUT_DURATION}.\n\n @return connect timeout", + "key": "connect-timeout", + "method": "io.helidon.webserver.ConnectionConfig.Builder#connectTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "32768", + "description": "Socket receive buffer size.\n Default is {@value #DEFAULT_SO_BUFFER_SIZE}.\n\n @return buffer size, in bytes\n @see java.net.StandardSocketOptions#SO_RCVBUF", + "key": "receive-buffer-size", + "method": "io.helidon.webserver.ConnectionConfig.Builder#receiveBufferSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Socket reuse address.\n Default is `true`.\n\n @return whether to reuse address\n @see java.net.StandardSocketOptions#SO_REUSEADDR", + "key": "reuse-address", + "method": "io.helidon.webserver.ConnectionConfig.Builder#reuseAddress(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Disable Nagle's algorithm by setting\n TCP_NODELAY to true. This can result in better performance on Mac or newer linux kernels for some\n payload types.\n Default is `false`.\n\n @return whether to use TCP_NODELAY, defaults to `false`\n @see java.net.StandardSocketOptions#TCP_NODELAY", + "key": "tcp-no-delay", + "method": "io.helidon.webserver.ConnectionConfig.Builder#tcpNoDelay(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "32768", + "description": "Socket send buffer size.\n Default is {@value #DEFAULT_SO_BUFFER_SIZE}.\n\n @return buffer size, in bytes\n @see java.net.StandardSocketOptions#SO_SNDBUF", + "key": "send-buffer-size", + "method": "io.helidon.webserver.ConnectionConfig.Builder#sendBufferSize(int)", + "type": "java.lang.Integer" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.http1.Http1Config", + "type": "io.helidon.webserver.http1.Http1Config", + "producers": [ + "io.helidon.webserver.http1.Http1Config#create(io.helidon.common.config.Config)", + "io.helidon.webserver.http1.Http1Config#builder()" + ], + "provides": [ + "io.helidon.webserver.spi.ProtocolConfig" + ], + "options": [ + { + "defaultValue": "false", + "description": "Whether to validate headers.\n If set to false, any value is accepted, otherwise validates headers + known headers\n are validated by format\n (content length is always validated as it is part of protocol processing (other headers may be validated if\n features use them)).\n

    \n Defaults to `false` as user has control on the header creation.\n

    \n\n @return whether to validate headers", + "key": "validate-response-headers", + "method": "io.helidon.webserver.http1.Http1Config.Builder#validateResponseHeaders(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "2048", + "description": "Maximal size of received HTTP prologue (GET /path HTTP/1.1).\n\n @return maximal size in bytes", + "key": "max-prologue-length", + "method": "io.helidon.webserver.http1.Http1Config.Builder#maxPrologueLength(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Logging of sent packets. Uses trace and debug levels on logger of\n Http1LoggingConnectionListener with suffix of `.send``.\n\n @return `true` if logging should be enabled for sent packets, `false` if no logging should be done", + "key": "send-log", + "method": "io.helidon.webserver.http1.Http1Config.Builder#sendLog(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Whether to validate headers.\n If set to false, any value is accepted, otherwise validates headers + known headers\n are validated by format\n (content length is always validated as it is part of protocol processing (other headers may be validated if\n features use them)).\n

    \n Defaults to `true`.\n

    \n\n @return whether to validate headers", + "key": "validate-request-headers", + "method": "io.helidon.webserver.http1.Http1Config.Builder#validateRequestHeaders(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Requested URI discovery settings.\n\n @return settings for computing the requested URI", + "key": "requested-uri-discovery", + "method": "io.helidon.webserver.http1.Http1Config.Builder#requestedUriDiscovery(io.helidon.http.RequestedUriDiscoveryContext)", + "type": "io.helidon.http.RequestedUriDiscoveryContext" + }, + { + "defaultValue": "16384", + "description": "Maximal size of received headers in bytes.\n\n @return maximal header size", + "key": "max-headers-size", + "method": "io.helidon.webserver.http1.Http1Config.Builder#maxHeadersSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "If set to false, any path is accepted (even containing illegal characters).\n\n @return whether to validate path", + "key": "validate-path", + "method": "io.helidon.webserver.http1.Http1Config.Builder#validatePath(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Logging of received packets. Uses trace and debug levels on logger of\n Http1LoggingConnectionListener with suffix of `.recv``.\n\n @return `true` if logging should be enabled for received packets, `false` if no logging should be done", + "key": "recv-log", + "method": "io.helidon.webserver.http1.Http1Config.Builder#receiveLog(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "When true WebServer answers to expect continue with 100 continue immediately,\n not waiting for user to actually request the data.\n\n @return if `true` answer with 100 continue immediately after expect continue", + "key": "continue-immediately", + "method": "io.helidon.webserver.http1.Http1Config.Builder#continueImmediately(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.ListenerConfig", + "type": "io.helidon.webserver.ListenerConfig", + "producers": [ + "io.helidon.webserver.ListenerConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.ListenerConfig#builder()" + ], + "options": [ + { + "defaultValue": "-1", + "description": "Limits the number of connections that can be opened at a single point in time.\n Defaults to `-1`, meaning \"unlimited\" - what the system allows.\n\n @return number of TCP connections that can be opened to this listener, regardless of protocol", + "key": "max-tcp-connections", + "method": "io.helidon.webserver.ListenerConfig.Builder#maxTcpConnections(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "0", + "description": "Number of buffers queued for write operations.\n\n @return maximal number of queued writes, defaults to 0", + "key": "write-queue-length", + "method": "io.helidon.webserver.ListenerConfig.Builder#writeQueueLength(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "512", + "description": "Initial buffer size in bytes of java.io.BufferedOutputStream created internally to\n write data to a socket connection. Default is `512`.\n\n @return initial buffer size used for writing", + "key": "write-buffer-size", + "method": "io.helidon.webserver.ListenerConfig.Builder#writeBufferSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "PT0.5S", + "description": "Grace period in ISO 8601 duration format to allow running tasks to complete before listener's shutdown.\n Default is `500` milliseconds.\n

    Configuration file values example: `PT0.5S`, `PT2S`.\n\n @return grace period", + "key": "shutdown-grace-period", + "method": "io.helidon.webserver.ListenerConfig.Builder#shutdownGracePeriod(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "Configure the listener specific io.helidon.http.encoding.ContentEncodingContext.\n This method discards all previously registered ContentEncodingContext.\n If no content encoding context is registered, content encoding context of the webserver would be used.\n\n @return content encoding context", + "key": "content-encoding", + "method": "io.helidon.webserver.ListenerConfig.Builder#contentEncoding(java.util.Optional)", + "type": "io.helidon.http.encoding.ContentEncodingContext" + }, + { + "defaultValue": "PT5M", + "description": "How long should we wait before closing a connection that has no traffic on it.\n Defaults to `PT5M` (5 minutes). Note that the timestamp is refreshed max. once per second, so this setting\n would be useless if configured for shorter periods of time (also not a very good support for connection keep alive,\n if the connections are killed so soon anyway).\n\n @return timeout of idle connections", + "key": "idle-connection-timeout", + "method": "io.helidon.webserver.ListenerConfig.Builder#idleConnectionTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "-1", + "description": "Limits the number of requests that can be executed at the same time (the number of active virtual threads of requests).\n Defaults to `-1`, meaning \"unlimited\" - what the system allows.\n Also make sure that this number is higher than the expected time it takes to handle a single request in your application,\n as otherwise you may stop in-progress requests.\n\n @return number of requests that can be processed on this listener, regardless of protocol", + "key": "max-concurrent-requests", + "method": "io.helidon.webserver.ListenerConfig.Builder#maxConcurrentRequests(int)", + "type": "java.lang.Integer" + }, + { + "description": "Requested URI discovery context.\n\n @return discovery context", + "key": "requested-uri-discovery", + "method": "io.helidon.webserver.ListenerConfig.Builder#requestedUriDiscoveryContext(java.util.Optional)", + "type": "io.helidon.http.RequestedUriDiscoveryContext" + }, + { + "description": "Configure the listener specific io.helidon.http.media.MediaContext.\n This method discards all previously registered MediaContext.\n If no media context is registered, media context of the webserver would be used.\n\n @return media context", + "key": "media-context", + "method": "io.helidon.webserver.ListenerConfig.Builder#mediaContext(java.util.Optional)", + "type": "io.helidon.http.media.MediaContext" + }, + { + "defaultValue": "131072", + "description": "If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize\n performance when writing it.\n If bigger, streaming will be used.\n

    \n Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such\n cases, this option is ignored.\n

    \n Default is 128Kb.\n\n @return maximal number of bytes to buffer in memory for supported writers", + "key": "max-in-memory-entity", + "method": "io.helidon.webserver.ListenerConfig.Builder#maxInMemoryEntity(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "-1", + "description": "Maximal number of bytes an entity may have.\n If io.helidon.http.HeaderNames#CONTENT_LENGTH is used, this is checked immediately,\n if io.helidon.http.HeaderValues#TRANSFER_ENCODING_CHUNKED is used, we will fail when the\n number of bytes read would exceed the max payload size.\n Defaults to unlimited (`-1`).\n\n @return maximal number of bytes of entity", + "key": "max-payload-size", + "method": "io.helidon.webserver.ListenerConfig.Builder#maxPayloadSize(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "1024", + "description": "Accept backlog.\n\n @return backlog", + "key": "backlog", + "method": "io.helidon.webserver.ListenerConfig.Builder#backlog(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "0", + "description": "Port of the default socket.\n If configured to `0` (the default), server starts on a random port.\n\n @return port to listen on (for the default socket)", + "key": "port", + "method": "io.helidon.webserver.ListenerConfig.Builder#port(int)", + "type": "java.lang.Integer" + }, + { + "description": "Listener receive buffer size.\n\n @return buffer size in bytes", + "key": "receive-buffer-size", + "method": "io.helidon.webserver.ListenerConfig.Builder#receiveBufferSize(java.util.Optional)", + "type": "java.lang.Integer" + }, + { + "description": "Options for connections accepted by this listener.\n This is not used to setup server connection.\n\n @return socket options", + "key": "connection-options", + "method": "io.helidon.webserver.ListenerConfig.Builder#connectionOptions(io.helidon.common.socket.SocketOptions)", + "type": "io.helidon.common.socket.SocketOptions" + }, + { + "defaultValue": "0.0.0.0", + "description": "Host of the default socket. Defaults to all host addresses (`0.0.0.0`).\n\n @return host address to listen on (for the default socket)", + "key": "host", + "method": "io.helidon.webserver.ListenerConfig.Builder#host(java.lang.String)" + }, + { + "defaultValue": "@default", + "description": "Name of this socket. Defaults to `@default`.\n Must be defined if more than one socket is needed.\n\n @return name of the socket", + "key": "name", + "method": "io.helidon.webserver.ListenerConfig.Builder#name(java.lang.String)" + }, + { + "defaultValue": "PT2M", + "description": "How often should we check for #idleConnectionTimeout().\n Defaults to `PT2M` (2 minutes).\n\n @return period of checking for idle connections", + "key": "idle-connection-period", + "method": "io.helidon.webserver.ListenerConfig.Builder#idleConnectionPeriod(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "Listener TLS configuration.\n\n @return tls of this configuration", + "key": "tls", + "method": "io.helidon.webserver.ListenerConfig.Builder#tls(java.util.Optional)", + "type": "io.helidon.common.tls.Tls" + }, + { + "description": "Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1.\n As the order is not important (providers are ordered by weight by default), we can use a configuration as an object,\n such as:\n

    \n protocols:\n   providers:\n     http_1_1:\n       max-prologue-length: 8192\n     http_2:\n       max-frame-size: 4096\n     websocket:\n       ....\n 
    \n\n @return all defined protocol configurations, loaded from service loader by default", + "key": "protocols", + "kind": "LIST", + "method": "io.helidon.webserver.ListenerConfig.Builder#protocols(java.util.List)", + "providerType": "io.helidon.webserver.spi.ProtocolConfigProvider", + "type": "io.helidon.webserver.spi.ProtocolConfig", + "provider": true + }, + { + "description": "Configuration of a connection (established from client against our server).\n\n @return connection configuration", + "key": "connection-config", + "method": "io.helidon.webserver.ListenerConfig.Builder#connectionConfig(java.util.Optional)", + "type": "io.helidon.webserver.ConnectionConfig" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.servicecommon", + "types": [ + { + "annotatedType": "io.helidon.webserver.servicecommon.HelidonFeatureSupport.Builder", + "type": "io.helidon.webserver.servicecommon.HelidonFeatureSupport.Builder", + "options": [ + { + "description": "Set the root context for the REST API of the service.", + "key": "web-context", + "method": "io.helidon.webserver.servicecommon.HelidonFeatureSupport.Builder#webContext(java.lang.String)" + }, + { + "description": "Set the CORS config from the specified `CrossOriginConfig` object.", + "key": "cross-origin-config", + "method": "io.helidon.webserver.servicecommon.HelidonFeatureSupport.Builder#crossOriginConfig(io.helidon.cors.CrossOriginConfig)", + "type": "io.helidon.cors.CrossOriginConfig" + } + ] + }, + { + "annotatedType": "io.helidon.webserver.servicecommon.RestServiceSettings.Builder", + "type": "io.helidon.webserver.servicecommon.RestServiceSettings.Builder", + "options": [ + { + "description": "Sets the web context to use for the service's endpoint.", + "key": "web-context", + "method": "io.helidon.webserver.servicecommon.RestServiceSettings.Builder#webContext(java.lang.String)", + "merge": true + }, + { + "description": "Sets the routing name to use for setting up the service's endpoint.", + "key": "routing", + "method": "io.helidon.webserver.servicecommon.RestServiceSettings.Builder#routing(java.lang.String)", + "merge": true + }, + { + "description": "Sets the cross-origin config builder for use in establishing CORS support for the service endpoints.", + "key": "cors", + "kind": "MAP", + "method": "io.helidon.webserver.servicecommon.RestServiceSettings.Builder#crossOriginConfig(io.helidon.cors.CrossOriginConfig.Builder)", + "type": "io.helidon.cors.CrossOriginConfig" + }, + { + "defaultValue": "true", + "description": "Is this service enabled or not.", + "key": "enabled", + "method": "io.helidon.webserver.servicecommon.RestServiceSettings.Builder#enabled(boolean)", + "type": "java.lang.Boolean", + "merge": true + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.webserver.cors", + "types": [ + { + "annotatedType": "io.helidon.webserver.cors.CorsConfig", + "prefix": "cors", + "type": "io.helidon.webserver.cors.CorsFeature", + "producers": [ + "io.helidon.webserver.cors.CorsConfig#create(io.helidon.common.config.Config)", + "io.helidon.webserver.cors.CorsConfig#builder()", + "io.helidon.webserver.cors.CorsFeature#create(io.helidon.webserver.cors.CorsConfig)" + ], + "provides": [ + "io.helidon.webserver.spi.ServerFeatureProvider" + ], + "options": [ + { + "description": "List of sockets to register this feature on. If empty, it would get registered on all sockets.\n\n @return socket names to register on, defaults to empty (all available sockets)", + "key": "sockets", + "kind": "LIST", + "method": "io.helidon.webserver.cors.CorsConfig.Builder#sockets(java.util.Set)" + }, + { + "defaultValue": "950.0", + "description": "Weight of the CORS feature. As it is used by other features, the default is quite high:\n {@value CorsFeature#WEIGHT}.\n\n @return weight of the feature", + "key": "weight", + "method": "io.helidon.webserver.cors.CorsConfig.Builder#weight(double)", + "type": "java.lang.Double" + }, + { + "description": "This feature can be disabled.\n\n @return whether the feature is enabled", + "key": "enabled", + "method": "io.helidon.webserver.cors.CorsConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean", + "required": true + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.common.configurable", + "types": [ + { + "annotatedType": "io.helidon.common.configurable.ResourceConfig", + "type": "io.helidon.common.configurable.Resource", + "producers": [ + "io.helidon.common.configurable.ResourceConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.configurable.ResourceConfig#builder()", + "io.helidon.common.configurable.Resource#create(io.helidon.common.configurable.ResourceConfig)" + ], + "options": [ + { + "description": "Resource is located on filesystem.\n\n @return path of the resource", + "key": "path", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#path(java.util.Optional)", + "type": "java.nio.file.Path" + }, + { + "description": "Resource is located on classpath.\n\n @return classpath location of the resource", + "key": "resource-path", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#resourcePath(java.util.Optional)" + }, + { + "description": "Host of the proxy when using URI.\n\n @return proxy host", + "key": "proxy-host", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#proxyHost(java.util.Optional)" + }, + { + "description": "Resource is available on a java.net.URI.\n\n @return of the resource\n @see #proxy()\n @see #useProxy()", + "key": "uri", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#uri(java.util.Optional)", + "type": "java.net.URI" + }, + { + "defaultValue": "true", + "description": "Whether to use proxy. If set to `false`, proxy will not be used even if configured.\n When set to `true` (default), proxy will be used if configured.\n\n @return whether to use proxy if configured", + "key": "use-proxy", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#useProxy(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Plain content of the resource (text).\n\n @return plain content", + "key": "content-plain", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#contentPlain(java.util.Optional)" + }, + { + "defaultValue": "80", + "description": "Port of the proxy when using URI.\n\n @return proxy port", + "key": "proxy-port", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#proxyPort(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "", + "description": "Description of this resource when configured through plain text or binary.\n\n @return description", + "key": "description", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#description(java.lang.String)" + }, + { + "description": "Binary content of the resource (base64 encoded).\n\n @return binary content", + "key": "content", + "method": "io.helidon.common.configurable.ResourceConfig.Builder#content(java.util.Optional)" + } + ] + }, + { + "annotatedType": "io.helidon.common.configurable.ScheduledThreadPoolConfig", + "type": "io.helidon.common.configurable.ScheduledThreadPoolSupplier", + "producers": [ + "io.helidon.common.configurable.ScheduledThreadPoolConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.configurable.ScheduledThreadPoolConfig#builder()", + "io.helidon.common.configurable.ScheduledThreadPoolSupplier#create(io.helidon.common.configurable.ScheduledThreadPoolConfig)" + ], + "options": [ + { + "defaultValue": "false", + "description": "Whether to prestart core threads in this thread pool executor.\n Defaults to {@value #DEFAULT_PRESTART}.\n\n @return whether to prestart the threads", + "key": "prestart", + "method": "io.helidon.common.configurable.ScheduledThreadPoolConfig.Builder#prestart(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "helidon-", + "description": "Name prefix for threads in this thread pool executor.\n Defaults to {@value #DEFAULT_THREAD_NAME_PREFIX}.\n\n @return prefix of a thread name", + "key": "thread-name-prefix", + "method": "io.helidon.common.configurable.ScheduledThreadPoolConfig.Builder#threadNamePrefix(java.lang.String)" + }, + { + "description": "When configured to `true`, an unbounded virtual executor service (project Loom) will be used.\n

    \n If enabled, all other configuration options of this executor service are ignored!\n\n @return whether to use virtual threads or not, defaults to `false`", + "key": "virtual-threads", + "method": "io.helidon.common.configurable.ScheduledThreadPoolConfig.Builder#virtualThreads(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "16", + "description": "Core pool size of the thread pool executor.\n Defaults to {@value #DEFAULT_CORE_POOL_SIZE}.\n\n @return corePoolSize see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize()", + "key": "core-pool-size", + "method": "io.helidon.common.configurable.ScheduledThreadPoolConfig.Builder#corePoolSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Is daemon of the thread pool executor.\n Defaults to {@value #DEFAULT_IS_DAEMON}.\n\n @return whether the threads are daemon threads", + "key": "is-daemon", + "method": "io.helidon.common.configurable.ScheduledThreadPoolConfig.Builder#daemon(boolean)", + "type": "java.lang.Boolean" + } + ] + }, + { + "annotatedType": "io.helidon.common.configurable.LruCacheConfig", + "type": "io.helidon.common.configurable.LruCache", + "producers": [ + "io.helidon.common.configurable.LruCacheConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.configurable.LruCacheConfig#builder()", + "io.helidon.common.configurable.LruCache#create(io.helidon.common.configurable.LruCacheConfig)" + ], + "options": [ + { + "defaultValue": "10000", + "description": "Configure capacity of the cache. Defaults to {@value LruCache#DEFAULT_CAPACITY}.\n\n @return maximal number of records in the cache before the oldest one is removed", + "key": "capacity", + "method": "io.helidon.common.configurable.LruCacheConfig.Builder#capacity(int)", + "type": "java.lang.Integer" + } + ] + }, + { + "annotatedType": "io.helidon.common.configurable.ThreadPoolConfig", + "type": "io.helidon.common.configurable.ThreadPoolSupplier", + "producers": [ + "io.helidon.common.configurable.ThreadPoolConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.configurable.ThreadPoolConfig#builder()", + "io.helidon.common.configurable.ThreadPoolSupplier#create(io.helidon.common.configurable.ThreadPoolConfig)" + ], + "options": [ + { + "defaultValue": "50", + "description": "Max pool size of the thread pool executor.\n Defaults to {@value #DEFAULT_MAX_POOL_SIZE}.\n\n @return maxPoolSize see java.util.concurrent.ThreadPoolExecutor#getMaximumPoolSize()", + "key": "max-pool-size", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#maxPoolSize(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "PT3M", + "description": "Keep alive of the thread pool executor.\n Defaults to {@value #DEFAULT_KEEP_ALIVE}.\n\n @return keep alive see java.util.concurrent.ThreadPoolExecutor#getKeepAliveTime(java.util.concurrent.TimeUnit)", + "key": "keep-alive", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#keepAlive(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "Name prefix for threads in this thread pool executor.\n Defaults to {@value #DEFAULT_THREAD_NAME_PREFIX}.\n\n @return prefix of a thread name", + "key": "thread-name-prefix", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#threadNamePrefix(java.util.Optional)" + }, + { + "defaultValue": "true", + "description": "Whether to prestart core threads in this thread pool executor.\n Defaults to {@value #DEFAULT_PRESTART}.\n\n @return whether to prestart the threads", + "key": "should-prestart", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#shouldPrestart(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "When configured to `true`, an unbounded virtual executor service (project Loom) will be used.\n

    \n If enabled, all other configuration options of this executor service are ignored!\n\n @return whether to use virtual threads or not, defaults to `false`", + "key": "virtual-threads", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#virtualThreads(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "10", + "description": "Core pool size of the thread pool executor.\n Defaults to {@value #DEFAULT_CORE_POOL_SIZE}.\n\n @return corePoolSize see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize()", + "key": "core-pool-size", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#corePoolSize(int)", + "type": "java.lang.Integer" + }, + { + "description": "Name of this thread pool executor.\n\n @return the pool name", + "key": "name", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#name(java.util.Optional)" + }, + { + "defaultValue": "true", + "description": "Is daemon of the thread pool executor.\n Defaults to {@value #DEFAULT_IS_DAEMON}.\n\n @return whether the threads are daemon threads", + "key": "is-daemon", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#daemon(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "1000", + "description": "The queue size above which pool growth will be considered if the pool is not fixed size.\n Defaults to {@value #DEFAULT_GROWTH_THRESHOLD}.\n\n @return the growth threshold", + "key": "growth-threshold", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#growthThreshold(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "10000", + "description": "Queue capacity of the thread pool executor.\n Defaults to {@value #DEFAULT_QUEUE_CAPACITY}.\n\n @return capacity of the queue backing the executor", + "key": "queue-capacity", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#queueCapacity(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "0", + "description": "The percentage of task submissions that should result in adding threads, expressed as a value from 1 to 100. The\n rate applies only when all of the following are true:\n

      \n
    • the pool size is below the maximum, and
    • \n
    • there are no idle threads, and
    • \n
    • the number of tasks in the queue exceeds the `growthThreshold`
    • \n
    \n For example, a rate of 20 means that while these conditions are met one thread will be added for every 5 submitted\n tasks.\n

    \n Defaults to {@value #DEFAULT_GROWTH_RATE}\n\n @return the growth rate", + "key": "growth-rate", + "method": "io.helidon.common.configurable.ThreadPoolConfig.Builder#growthRate(int)", + "type": "java.lang.Integer" + } + ] + }, + { + "annotatedType": "io.helidon.common.configurable.AllowListConfig", + "type": "io.helidon.common.configurable.AllowList", + "producers": [ + "io.helidon.common.configurable.AllowListConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.configurable.AllowListConfig#builder()", + "io.helidon.common.configurable.AllowList#create(io.helidon.common.configurable.AllowListConfig)" + ], + "options": [ + { + "defaultValue": "false", + "description": "Allows all strings to match (subject to \"deny\" conditions). An `allow.all` setting of `false` does\n not deny all strings but rather represents the absence of a universal match, meaning that other allow and deny settings\n determine the matching outcomes.\n\n @return whether to allow all strings to match (subject to \"deny\" conditions)", + "key": "allow.all", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#allowAll(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Patterns specifying strings to allow.\n\n @return patterns which allow matching", + "key": "allow.pattern", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#allowedPatterns(java.util.List)", + "type": "java.util.regex.Pattern" + }, + { + "description": "Suffixes specifying strings to deny.\n\n @return suffixes which deny matching", + "key": "deny.suffix", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#deniedSuffixes(java.util.List)" + }, + { + "description": "Prefixes specifying strings to allow.\n\n @return prefixes which allow matching", + "key": "allow.prefix", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#allowedPrefixes(java.util.List)" + }, + { + "description": "Exact strings to deny.\n\n @return exact strings to allow", + "key": "deny.exact", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#denied(java.util.List)" + }, + { + "description": "Patterns specifying strings to deny.\n\n @return patterns which deny matching", + "key": "deny.pattern", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#deniedPatterns(java.util.List)", + "type": "java.util.regex.Pattern" + }, + { + "description": "Exact strings to allow.\n\n @return exact strings to allow", + "key": "allow.exact", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#allowed(java.util.List)" + }, + { + "description": "Prefixes specifying strings to deny.\n\n @return prefixes which deny matching", + "key": "deny.prefix", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#deniedPrefixes(java.util.List)" + }, + { + "description": "Suffixes specifying strings to allow.\n\n @return suffixes which allow matching", + "key": "allow.suffix", + "kind": "LIST", + "method": "io.helidon.common.configurable.AllowListConfig.Builder#allowedSuffixes(java.util.List)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.common.pki", + "types": [ + { + "annotatedType": "io.helidon.common.pki.Keys", + "type": "io.helidon.common.pki.Keys", + "producers": [ + "io.helidon.common.pki.Keys#create(io.helidon.common.config.Config)", + "io.helidon.common.pki.Keys#builder()" + ], + "options": [ + { + "description": "Configure keys from pem file(s).\n Once the config object is built, this option will ALWAYS be empty. All keys from the keystore will be\n populated to #privateKey(), #publicKey(), #publicCert() etc.\n\n @return pem based definition", + "key": "pem", + "method": "io.helidon.common.pki.Keys.Builder#pem(java.util.Optional)", + "type": "io.helidon.common.pki.PemKeys" + }, + { + "description": "Configure keys from a keystore.\n Once the config object is built, this option will ALWAYS be empty. All keys from the keystore will be\n populated to #privateKey(), #publicKey(), #publicCert() etc.\n\n @return keystore configuration", + "key": "keystore", + "method": "io.helidon.common.pki.Keys.Builder#keystore(java.util.Optional)", + "type": "io.helidon.common.pki.KeystoreKeys" + } + ] + }, + { + "annotatedType": "io.helidon.common.pki.KeystoreKeys", + "type": "io.helidon.common.pki.KeystoreKeys", + "producers": [ + "io.helidon.common.pki.KeystoreKeys#create(io.helidon.common.config.Config)", + "io.helidon.common.pki.KeystoreKeys#builder()" + ], + "options": [ + { + "defaultValue": "false", + "description": "If you want to build a trust store, call this method to add all\n certificates present in the keystore to certificate list.\n\n @return whether this is a trust store", + "key": "trust-store", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#trustStore(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Alias of X.509 certificate of public key.\n Used to load both the certificate and public key.\n\n @return alias under which the certificate is stored in the keystore", + "key": "cert.alias", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#certAlias(java.util.Optional)" + }, + { + "description": "Alias of an X.509 chain.\n\n @return alias of certificate chain in the keystore", + "key": "cert-chain.alias", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#certChainAlias(java.util.Optional)" + }, + { + "description": "Keystore resource definition.\n\n @return keystore resource, from file path, classpath, URL etc.", + "key": "resource", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#keystore(io.helidon.common.configurable.Resource)", + "type": "io.helidon.common.configurable.Resource", + "required": true + }, + { + "description": "Pass-phrase of the keystore (supported with JKS and PKCS12 keystores).\n\n @return keystore password to use", + "key": "passphrase", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#passphrase(java.util.Optional)", + "type": "char[]" + }, + { + "description": "Alias of the private key in the keystore.\n\n @return alias of the key in the keystore", + "key": "key.alias", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#keyAlias(java.util.Optional)" + }, + { + "description": "Pass-phrase of the key in the keystore (used for private keys).\n This is (by default) the same as keystore passphrase - only configure\n if it differs from keystore passphrase.\n\n @return pass-phrase of the key", + "key": "key.passphrase", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#keyPassphrase(java.util.Optional)", + "type": "char[]" + }, + { + "defaultValue": "PKCS12", + "description": "Set type of keystore.\n Defaults to {@value #DEFAULT_KEYSTORE_TYPE},\n expected are other keystore types supported by java then can store keys under aliases.\n\n @return keystore type to load the key", + "key": "type", + "method": "io.helidon.common.pki.KeystoreKeys.Builder#type(java.lang.String)" + } + ] + }, + { + "annotatedType": "io.helidon.common.pki.PemKeys", + "type": "io.helidon.common.pki.PemKeys", + "producers": [ + "io.helidon.common.pki.PemKeys#create(io.helidon.common.config.Config)", + "io.helidon.common.pki.PemKeys#builder()" + ], + "options": [ + { + "description": "Load certificate chain from PEM resource.\n\n @return resource (e.g. classpath, file path, URL etc.)", + "key": "cert-chain.resource", + "method": "io.helidon.common.pki.PemKeys.Builder#certChain(java.util.Optional)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "description": "Read one or more certificates in PEM format from a resource definition. Used eg: in a trust store.\n\n @return key resource (file, classpath, URL etc.)", + "key": "certificates.resource", + "method": "io.helidon.common.pki.PemKeys.Builder#certificates(java.util.Optional)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "description": "Read a private key from PEM format from a resource definition.\n\n @return key resource (file, classpath, URL etc.)", + "key": "key.resource", + "method": "io.helidon.common.pki.PemKeys.Builder#key(java.util.Optional)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "description": "Read a public key from PEM format from a resource definition.\n\n @return public key resource (file, classpath, URL etc.)", + "key": "public-key.resource", + "method": "io.helidon.common.pki.PemKeys.Builder#publicKey(java.util.Optional)", + "type": "io.helidon.common.configurable.Resource" + }, + { + "description": "Passphrase for private key. If the key is encrypted (and in PEM PKCS#8 format), this passphrase will be used to\n decrypt it.\n\n @return passphrase used to encrypt the private key", + "key": "key.passphrase", + "method": "io.helidon.common.pki.PemKeys.Builder#keyPassphrase(java.util.Optional)", + "type": "char[]" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.common.tls", + "types": [ + { + "annotatedType": "io.helidon.common.tls.TlsConfig", + "type": "io.helidon.common.tls.Tls", + "producers": [ + "io.helidon.common.tls.TlsConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.tls.TlsConfig#builder()", + "io.helidon.common.tls.Tls#create(io.helidon.common.tls.TlsConfig)" + ], + "options": [ + { + "defaultValue": "PT24H", + "description": "SSL session timeout.\n\n @return session timeout, defaults to {@value DEFAULT_SESSION_TIMEOUT}.", + "key": "session-timeout", + "method": "io.helidon.common.tls.TlsConfig.Builder#sessionTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "The Tls manager. If one is not explicitly defined in the config then a default manager will be created.\n\n @return the tls manager of the tls instance\n @see ConfiguredTlsManager", + "key": "manager", + "method": "io.helidon.common.tls.TlsConfig.Builder#manager(io.helidon.common.tls.TlsManager)", + "providerType": "io.helidon.common.tls.spi.TlsManagerProvider", + "type": "io.helidon.common.tls.TlsManager", + "provider": true + }, + { + "description": "Provider of the key stores used internally to create a key and trust manager factories.\n\n @return keystore provider, if not defined, provider is not specified", + "key": "internal-keystore-provider", + "method": "io.helidon.common.tls.TlsConfig.Builder#internalKeystoreProvider(java.util.Optional)" + }, + { + "description": "Private key to use. For server side TLS, this is required.\n For client side TLS, this is optional (used when mutual TLS is enabled).\n\n @return private key to use", + "key": "private-key", + "method": "io.helidon.common.tls.TlsConfig.Builder#privateKey(java.util.Optional)", + "type": "java.security.PrivateKey" + }, + { + "defaultValue": "HTTPS", + "description": "Identification algorithm for SSL endpoints.\n\n @return configure endpoint identification algorithm, or set to `NONE`\n to disable endpoint identification (equivalent to hostname verification).\n Defaults to {@value Tls#ENDPOINT_IDENTIFICATION_HTTPS}", + "key": "endpoint-identification-algorithm", + "method": "io.helidon.common.tls.TlsConfig.Builder#endpointIdentificationAlgorithm(java.lang.String)" + }, + { + "description": "Algorithm of the key manager factory used when private key is defined.\n Defaults to javax.net.ssl.KeyManagerFactory#getDefaultAlgorithm().\n\n @return algorithm to use", + "key": "key-manager-factory-algorithm", + "method": "io.helidon.common.tls.TlsConfig.Builder#keyManagerFactoryAlgorithm(java.util.Optional)" + }, + { + "description": "Provider to use when creating a new secure random.\n When defined, #secureRandomAlgorithm() must be defined as well.\n\n @return provider to use, by default no provider is specified", + "key": "secure-random-provider", + "method": "io.helidon.common.tls.TlsConfig.Builder#secureRandomProvider(java.util.Optional)" + }, + { + "defaultValue": "20480", + "description": "SSL session cache size.\n\n @return session cache size, defaults to {@value DEFAULT_SESSION_CACHE_SIZE}.", + "key": "session-cache-size", + "method": "io.helidon.common.tls.TlsConfig.Builder#sessionCacheSize(int)", + "type": "java.lang.Integer" + }, + { + "description": "List of certificates that form the trust manager.\n\n @return certificates to be trusted", + "key": "trust", + "kind": "LIST", + "method": "io.helidon.common.tls.TlsConfig.Builder#trust(java.util.List)", + "type": "java.security.cert.X509Certificate" + }, + { + "defaultValue": "true", + "description": "Flag indicating whether Tls is enabled.\n\n @return enabled flag", + "key": "enabled", + "method": "io.helidon.common.tls.TlsConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Certificate revocation check configuration.\n\n @return certificate revocation configuration", + "key": "revocation", + "method": "io.helidon.common.tls.TlsConfig.Builder#revocation(java.util.Optional)", + "type": "io.helidon.common.tls.RevocationConfig" + }, + { + "description": "Use explicit provider to obtain an instance of javax.net.ssl.SSLContext.\n\n @return provider to use, defaults to none (only #protocol() is used by default)", + "key": "provider", + "method": "io.helidon.common.tls.TlsConfig.Builder#provider(java.util.Optional)" + }, + { + "description": "Enabled cipher suites for TLS communication.\n\n @return cipher suits to enable, by default (or if list is empty), all available cipher suites\n are enabled", + "key": "cipher-suite", + "kind": "LIST", + "method": "io.helidon.common.tls.TlsConfig.Builder#enabledCipherSuites(java.util.List)" + }, + { + "defaultValue": "NONE", + "description": "Configure requirement for mutual TLS.\n\n @return what type of mutual TLS to use, defaults to TlsClientAuth#NONE", + "key": "client-auth", + "method": "io.helidon.common.tls.TlsConfig.Builder#clientAuth(io.helidon.common.tls.TlsClientAuth)", + "type": "io.helidon.common.tls.TlsClientAuth", + "allowedValues": [ + { + "description": "Mutual TLS is required.\n Server MUST present a certificate trusted by the client, client MUST present a certificate trusted by the server.\n This implies private key and trust configuration for both server and client.", + "value": "REQUIRED" + }, + { + "description": "Mutual TLS is optional.\n Server MUST present a certificate trusted by the client, client MAY present a certificate trusted by the server.\n This implies private key configuration at least for server, trust configuration for at least client.", + "value": "OPTIONAL" + }, + { + "description": "Mutual TLS is disabled.\n Server MUST present a certificate trusted by the client, client does not present a certificate.\n This implies private key configuration for server, trust configuration for client.", + "value": "NONE" + } + ] + }, + { + "description": "Trust manager factory algorithm.\n\n @return algorithm to use", + "key": "trust-manager-factory-algorithm", + "method": "io.helidon.common.tls.TlsConfig.Builder#trustManagerFactoryAlgorithm(java.util.Optional)" + }, + { + "description": "Type of the key stores used internally to create a key and trust manager factories.\n\n @return keystore type, defaults to java.security.KeyStore#getDefaultType()", + "key": "internal-keystore-type", + "method": "io.helidon.common.tls.TlsConfig.Builder#internalKeystoreType(java.util.Optional)" + }, + { + "defaultValue": "false", + "description": "Trust any certificate provided by the other side of communication.\n

    \n This is a dangerous setting: if set to `true`, any certificate will be accepted, throwing away\n most of the security advantages of TLS. NEVER do this in production.\n\n @return whether to trust all certificates, do not use in production", + "key": "trust-all", + "method": "io.helidon.common.tls.TlsConfig.Builder#trustAll(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "TLS", + "description": "Configure the protocol used to obtain an instance of javax.net.ssl.SSLContext.\n\n @return protocol to use, defaults to {@value DEFAULT_PROTOCOL}", + "key": "protocol", + "method": "io.helidon.common.tls.TlsConfig.Builder#protocol(java.lang.String)" + }, + { + "description": "Enabled protocols for TLS communication.\n Example of valid values for `TLS` protocol: `TLSv1.3`, `TLSv1.2`\n\n @return protocols to enable, by default (or if list is empty), all available protocols are enabled", + "key": "protocols", + "kind": "LIST", + "method": "io.helidon.common.tls.TlsConfig.Builder#enabledProtocols(java.util.List)" + }, + { + "description": "Algorithm to use when creating a new secure random.\n\n @return algorithm to use, by default uses java.security.SecureRandom constructor", + "key": "secure-random-algorithm", + "method": "io.helidon.common.tls.TlsConfig.Builder#secureRandomAlgorithm(java.util.Optional)" + } + ] + }, + { + "annotatedType": "io.helidon.common.tls.RevocationConfig", + "type": "io.helidon.common.tls.RevocationConfig", + "producers": [ + "io.helidon.common.tls.RevocationConfig#create(io.helidon.common.config.Config)", + "io.helidon.common.tls.RevocationConfig#builder()" + ], + "options": [ + { + "defaultValue": "false", + "description": "Allow revocation check to succeed if the revocation status cannot be\n determined for one of the following reasons:\n

      \n
    • The CRL or OCSP response cannot be obtained because of a\n network error.\n
    • The OCSP responder returns one of the following errors\n specified in section 2.3 of RFC 2560: internalError or tryLater.\n
    \n\n @return whether soft fail is enabled", + "key": "soft-fail-enabled", + "method": "io.helidon.common.tls.RevocationConfig.Builder#softFailEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "The URI that identifies the location of the OCSP responder. This\n overrides the `ocsp.responderURL` security property and any\n responder specified in a certificate's Authority Information Access\n Extension, as defined in RFC 5280.\n\n @return OCSP responder URI", + "key": "ocsp-responder-uri", + "method": "io.helidon.common.tls.RevocationConfig.Builder#ocspResponderUri(java.util.Optional)", + "type": "java.net.URI" + }, + { + "defaultValue": "false", + "description": "Prefer CRL over OCSP.\n Default value is `false`. OCSP is preferred over the CRL by default.\n\n @return whether to prefer CRL over OCSP", + "key": "prefer-crl-over-ocsp", + "method": "io.helidon.common.tls.RevocationConfig.Builder#preferCrlOverOcsp(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Only check the revocation status of end-entity certificates.\n Default value is `false`.\n\n @return whether to check only end-entity certificates", + "key": "check-only-end-entity", + "method": "io.helidon.common.tls.RevocationConfig.Builder#checkOnlyEndEntity(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "description": "Enable fallback to the less preferred checking option.\n
    \n If the primary method for revocation checking fails to verify the revocation status of a certificate\n (such as using a CRL or OCSP), the checker will attempt alternative methods. This option ensures\n whether revocation checking is performed strictly according to the specified method, or should fallback\n to the one less preferred. OCSP is preferred over the CRL by default.\n\n @return whether to allow fallback to the less preferred checking option", + "key": "fallback-enabled", + "method": "io.helidon.common.tls.RevocationConfig.Builder#fallbackEnabled(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "false", + "description": "Flag indicating whether this revocation config is enabled.\n\n @return enabled flag", + "key": "enabled", + "method": "io.helidon.common.tls.RevocationConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.common.socket", + "types": [ + { + "annotatedType": "io.helidon.common.socket.SocketOptions", + "type": "io.helidon.common.socket.SocketOptions", + "producers": [ + "io.helidon.common.socket.SocketOptions#create(io.helidon.common.config.Config)", + "io.helidon.common.socket.SocketOptions#builder()" + ], + "options": [ + { + "defaultValue": "PT30S", + "description": "Socket read timeout. Default is 30 seconds.\n\n @return read timeout duration", + "key": "read-timeout", + "method": "io.helidon.common.socket.SocketOptions.Builder#readTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "description": "Socket send buffer size.\n\n @return buffer size, in bytes\n @see java.net.StandardSocketOptions#SO_SNDBUF", + "key": "socket-send-buffer-size", + "method": "io.helidon.common.socket.SocketOptions.Builder#socketSendBufferSize(java.util.Optional)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Socket reuse address.\n Default is `true`.\n\n @return whether to reuse address\n @see java.net.StandardSocketOptions#SO_REUSEADDR", + "key": "socket-reuse-address", + "method": "io.helidon.common.socket.SocketOptions.Builder#socketReuseAddress(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "PT10S", + "description": "Socket connect timeout. Default is 10 seconds.\n\n @return connect timeout duration", + "key": "connect-timeout", + "method": "io.helidon.common.socket.SocketOptions.Builder#connectTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "false", + "description": "This option may improve performance on some systems.\n Default is `false`.\n\n @return whether to use TCP_NODELAY, defaults to `false`\n @see java.net.StandardSocketOptions#TCP_NODELAY", + "key": "tcp-no-delay", + "method": "io.helidon.common.socket.SocketOptions.Builder#tcpNoDelay(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Socket receive buffer size.\n\n @return buffer size, in bytes\n @see java.net.StandardSocketOptions#SO_RCVBUF", + "key": "socket-receive-buffer-size", + "method": "io.helidon.common.socket.SocketOptions.Builder#socketReceiveBufferSize(java.util.Optional)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "true", + "description": "Configure socket keep alive.\n Default is `true`.\n\n @return keep alive\n @see java.net.StandardSocketOptions#SO_KEEPALIVE", + "key": "socket-keep-alive", + "method": "io.helidon.common.socket.SocketOptions.Builder#socketKeepAlive(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.http.encoding", + "types": [ + { + "annotatedType": "io.helidon.http.encoding.ContentEncodingContextConfig", + "type": "io.helidon.http.encoding.ContentEncodingContext", + "producers": [ + "io.helidon.http.encoding.ContentEncodingContextConfig#create(io.helidon.common.config.Config)", + "io.helidon.http.encoding.ContentEncodingContextConfig#builder()", + "io.helidon.http.encoding.ContentEncodingContext#create(io.helidon.http.encoding.ContentEncodingContextConfig)" + ], + "options": [ + { + "description": "List of content encodings that should be used.\n Encodings configured here have priority over encodings discovered through service loader.\n\n @return list of content encodings to be used (such as `gzip,deflate`)", + "key": "content-encodings", + "kind": "LIST", + "method": "io.helidon.http.encoding.ContentEncodingContextConfig.Builder#contentEncodings(java.util.List)", + "providerType": "io.helidon.http.encoding.spi.ContentEncodingProvider", + "type": "io.helidon.http.encoding.ContentEncoding", + "provider": true + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.http", + "types": [ + { + "annotatedType": "io.helidon.http.RequestedUriDiscoveryContext.Builder", + "type": "io.helidon.http.RequestedUriDiscoveryContext", + "producers": [ + "io.helidon.http.RequestedUriDiscoveryContext.Builder#build()", + "io.helidon.http.RequestedUriDiscoveryContext#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "description": "Sets the discovery types for requested URI discovery for requests arriving on the socket.", + "key": "types", + "kind": "LIST", + "method": "io.helidon.http.RequestedUriDiscoveryContext.Builder#types(java.util.List)", + "type": "io.helidon.http.RequestedUriDiscoveryContext.RequestedUriDiscoveryType", + "allowedValues": [ + { + "description": "The `io.helidon.http.Header#FORWARDED` header is used to discover the original requested URI.", + "value": "FORWARDED" + }, + { + "description": "The\n `io.helidon.http.Header#X_FORWARDED_PROTO`,\n `io.helidon.http.Header#X_FORWARDED_HOST`,\n `io.helidon.http.Header#X_FORWARDED_PORT`,\n `io.helidon.http.Header#X_FORWARDED_PREFIX`\n headers are used to discover the original requested URI.", + "value": "X_FORWARDED" + }, + { + "description": "This is the default, only the `io.helidon.http.Header#HOST` header is used to discover\n requested URI.", + "value": "HOST" + } + ] + }, + { + "description": "Sets the trusted proxies for requested URI discovery for requests arriving on the socket.", + "key": "trusted-proxies", + "method": "io.helidon.http.RequestedUriDiscoveryContext.Builder#trustedProxies(io.helidon.common.configurable.AllowList)", + "type": "io.helidon.common.configurable.AllowList" + }, + { + "defaultValue": "true if 'types' or 'trusted-proxies' is set; false otherwise", + "description": "Sets whether requested URI discovery is enabled for requestes arriving on the socket.", + "key": "enabled", + "method": "io.helidon.http.RequestedUriDiscoveryContext.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.http.media", + "types": [ + { + "annotatedType": "io.helidon.http.media.MediaContextConfig", + "type": "io.helidon.http.media.MediaContext", + "producers": [ + "io.helidon.http.media.MediaContextConfig#create(io.helidon.common.config.Config)", + "io.helidon.http.media.MediaContextConfig#builder()", + "io.helidon.http.media.MediaContext#create(io.helidon.http.media.MediaContextConfig)" + ], + "options": [ + { + "defaultValue": "true", + "description": "Should we register defaults of Helidon, such as String media support.\n\n @return whether to register default media supports", + "key": "register-defaults", + "method": "io.helidon.http.media.MediaContextConfig.Builder#registerDefaults(boolean)", + "type": "java.lang.Boolean" + }, + { + "description": "Media supports to use.\n This instance has priority over provider(s) discovered by service loader.\n The providers are used in order of calling this method, where the first support added is the\n first one to be queried for readers and writers.\n\n @return media supports", + "key": "media-supports", + "kind": "LIST", + "method": "io.helidon.http.media.MediaContextConfig.Builder#mediaSupports(java.util.List)", + "providerType": "io.helidon.http.media.spi.MediaSupportProvider", + "type": "io.helidon.http.media.MediaSupport", + "provider": true + }, + { + "description": "Existing context to be used as a fallback for this context.\n\n @return media context to use if supports configured on this request cannot provide a good result", + "key": "fallback", + "method": "io.helidon.http.media.MediaContextConfig.Builder#fallback(java.util.Optional)", + "type": "io.helidon.http.media.MediaContext" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.faulttolerance", + "types": [ + { + "annotatedType": "io.helidon.faulttolerance.CircuitBreakerConfig", + "prefix": "fault-tolerance.circuit-breakers", + "type": "io.helidon.faulttolerance.CircuitBreaker", + "standalone": true, + "producers": [ + "io.helidon.faulttolerance.CircuitBreakerConfig#create(io.helidon.common.config.Config)", + "io.helidon.faulttolerance.CircuitBreakerConfig#builder()", + "io.helidon.faulttolerance.CircuitBreaker#create(io.helidon.faulttolerance.CircuitBreakerConfig)" + ], + "options": [ + { + "defaultValue": "PT5S", + "description": "How long to wait before transitioning from open to half-open state.\n\n @return delay", + "key": "delay", + "method": "io.helidon.faulttolerance.CircuitBreakerConfig.Builder#delay(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "10", + "description": "Rolling window size used to calculate ratio of failed requests.\n Default is {@value #DEFAULT_VOLUME}.\n\n @return how big a window is used to calculate error errorRatio\n @see #errorRatio()", + "key": "volume", + "method": "io.helidon.faulttolerance.CircuitBreakerConfig.Builder#volume(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "60", + "description": "How many failures out of 100 will trigger the circuit to open.\n This is adapted to the #volume() used to handle the window of requests.\n

    If errorRatio is 40, and volume is 10, 4 failed requests will open the circuit.\n Default is {@value #DEFAULT_ERROR_RATIO}.\n\n @return percent of failure that trigger the circuit to open\n @see #volume()", + "key": "error-ratio", + "method": "io.helidon.faulttolerance.CircuitBreakerConfig.Builder#errorRatio(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "1", + "description": "How many successful calls will close a half-open circuit.\n Nevertheless, the first failed call will open the circuit again.\n Default is {@value #DEFAULT_SUCCESS_THRESHOLD}.\n\n @return number of calls", + "key": "success-threshold", + "method": "io.helidon.faulttolerance.CircuitBreakerConfig.Builder#successThreshold(int)", + "type": "java.lang.Integer" + } + ] + }, + { + "annotatedType": "io.helidon.faulttolerance.AsyncConfig", + "type": "io.helidon.faulttolerance.Async", + "producers": [ + "io.helidon.faulttolerance.AsyncConfig#create(io.helidon.common.config.Config)", + "io.helidon.faulttolerance.AsyncConfig#builder()", + "io.helidon.faulttolerance.Async#create(io.helidon.faulttolerance.AsyncConfig)" + ], + "options": [ + { + "description": "Name of an executor service. This is only honored when service registry is used.\n\n @return name fo the java.util.concurrent.ExecutorService to lookup\n @see #executor()", + "key": "executor-name", + "method": "io.helidon.faulttolerance.AsyncConfig.Builder#executorName(java.util.Optional)" + } + ] + }, + { + "annotatedType": "io.helidon.faulttolerance.BulkheadConfig", + "prefix": "fault-tolerance.bulkheads", + "type": "io.helidon.faulttolerance.Bulkhead", + "standalone": true, + "producers": [ + "io.helidon.faulttolerance.BulkheadConfig#create(io.helidon.common.config.Config)", + "io.helidon.faulttolerance.BulkheadConfig#builder()", + "io.helidon.faulttolerance.Bulkhead#create(io.helidon.faulttolerance.BulkheadConfig)" + ], + "options": [ + { + "defaultValue": "10", + "description": "Maximal number of parallel requests going through this bulkhead.\n When the limit is reached, additional requests are enqueued.\n\n @return maximal number of parallel calls, defaults is {@value DEFAULT_LIMIT}", + "key": "limit", + "method": "io.helidon.faulttolerance.BulkheadConfig.Builder#limit(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "10", + "description": "Maximal number of enqueued requests waiting for processing.\n When the limit is reached, additional attempts to invoke\n a request will receive a BulkheadException.\n\n @return length of the queue", + "key": "queue-length", + "method": "io.helidon.faulttolerance.BulkheadConfig.Builder#queueLength(int)", + "type": "java.lang.Integer" + } + ] + }, + { + "annotatedType": "io.helidon.faulttolerance.TimeoutConfig", + "prefix": "fault-tolerance.timeouts", + "type": "io.helidon.faulttolerance.Timeout", + "standalone": true, + "producers": [ + "io.helidon.faulttolerance.TimeoutConfig#create(io.helidon.common.config.Config)", + "io.helidon.faulttolerance.TimeoutConfig#builder()", + "io.helidon.faulttolerance.Timeout#create(io.helidon.faulttolerance.TimeoutConfig)" + ], + "options": [ + { + "defaultValue": "false", + "description": "Flag to indicate that code must be executed in current thread instead\n of in an executor's thread. This flag is `false` by default.\n\n @return whether to execute on current thread (`true`), or in an executor service (`false`})", + "key": "current-thread", + "method": "io.helidon.faulttolerance.TimeoutConfig.Builder#currentThread(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "PT10S", + "description": "Duration to wait before timing out.\n Defaults to `10 seconds`.\n\n @return timeout", + "key": "timeout", + "method": "io.helidon.faulttolerance.TimeoutConfig.Builder#timeout(java.time.Duration)", + "type": "java.time.Duration" + } + ] + }, + { + "annotatedType": "io.helidon.faulttolerance.RetryConfig", + "prefix": "fault-tolerance.retries", + "type": "io.helidon.faulttolerance.Retry", + "standalone": true, + "producers": [ + "io.helidon.faulttolerance.RetryConfig#create(io.helidon.common.config.Config)", + "io.helidon.faulttolerance.RetryConfig#builder()", + "io.helidon.faulttolerance.Retry#create(io.helidon.faulttolerance.RetryConfig)" + ], + "options": [ + { + "defaultValue": "PT1S", + "description": "Overall timeout of all retries combined.\n\n @return overall timeout", + "key": "overall-timeout", + "method": "io.helidon.faulttolerance.RetryConfig.Builder#overallTimeout(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "PT0.2S", + "description": "Base delay between try and retry.\n Defaults to `200 ms`.\n\n @return delay between retries (combines with retry policy)", + "key": "delay", + "method": "io.helidon.faulttolerance.RetryConfig.Builder#delay(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "3", + "description": "Number of calls (first try + retries).\n\n @return number of desired calls, must be 1 (means no retries) or higher.", + "key": "calls", + "method": "io.helidon.faulttolerance.RetryConfig.Builder#calls(int)", + "type": "java.lang.Integer" + }, + { + "defaultValue": "PT-1S", + "description": "Jitter for Retry.JitterRetryPolicy. If unspecified (value of `-1`),\n delaying retry policy is used. If both this value, and #delayFactor() are specified, delaying retry policy\n would be used.\n\n @return jitter", + "key": "jitter", + "method": "io.helidon.faulttolerance.RetryConfig.Builder#jitter(java.time.Duration)", + "type": "java.time.Duration" + }, + { + "defaultValue": "-1.0", + "description": "Delay retry policy factor. If unspecified (value of `-1`), Jitter retry policy would be used, unless\n jitter is also unspecified.\n

    \n Default when Retry.DelayingRetryPolicy is used is `2`.\n\n @return delay factor for delaying retry policy", + "key": "delay-factor", + "method": "io.helidon.faulttolerance.RetryConfig.Builder#delayFactor(double)", + "type": "java.lang.Double" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.cors", + "types": [ + { + "annotatedType": "io.helidon.cors.CrossOriginConfig.Builder", + "type": "io.helidon.cors.CrossOriginConfig", + "producers": [ + "io.helidon.cors.CrossOriginConfig.Builder#build()", + "io.helidon.cors.CrossOriginConfig#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "defaultValue": "{+}", + "description": "Updates the path prefix for this cross-origin config.", + "key": "path-pattern", + "method": "io.helidon.cors.CrossOriginConfig.Builder#pathPattern(java.lang.String)" + }, + { + "defaultValue": "*", + "description": "Sets the allow headers.", + "key": "allow-headers", + "kind": "LIST", + "method": "io.helidon.cors.CrossOriginConfig.Builder#allowHeaders(java.lang.String[])" + }, + { + "defaultValue": "3600", + "description": "Sets the maximum age.", + "key": "max-age-seconds", + "method": "io.helidon.cors.CrossOriginConfig.Builder#maxAgeSeconds(long)", + "type": "java.lang.Long" + }, + { + "defaultValue": "false", + "description": "Sets the allow credentials flag.", + "key": "allow-credentials", + "method": "io.helidon.cors.CrossOriginConfig.Builder#allowCredentials(boolean)", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "*", + "description": "Sets the allowOrigins.", + "key": "allow-origins", + "kind": "LIST", + "method": "io.helidon.cors.CrossOriginConfig.Builder#allowOrigins(java.lang.String[])" + }, + { + "description": "Sets the expose headers.", + "key": "expose-headers", + "kind": "LIST", + "method": "io.helidon.cors.CrossOriginConfig.Builder#exposeHeaders(java.lang.String[])" + }, + { + "defaultValue": "*", + "description": "Sets the allow methods.", + "key": "allow-methods", + "kind": "LIST", + "method": "io.helidon.cors.CrossOriginConfig.Builder#allowMethods(java.lang.String[])" + }, + { + "defaultValue": "true", + "description": "Sets whether this config should be enabled or not.", + "key": "enabled", + "method": "io.helidon.cors.CrossOriginConfig.Builder#enabled(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.microprofile.server", + "types": [ + { + "annotatedType": "io.helidon.microprofile.server.Server.Builder", + "description": "Configuration of Helidon Microprofile Server", + "prefix": "server", + "type": "io.helidon.microprofile.server.Server", + "standalone": true, + "producers": [ + "io.helidon.microprofile.server.Server.Builder#build()" + ], + "options": [ + { + "description": "Configure listen port.", + "key": "port", + "method": "io.helidon.microprofile.server.Server.Builder#port(int)", + "type": "java.lang.Integer" + }, + { + "description": "Configure listen host.", + "key": "host", + "method": "io.helidon.microprofile.server.Server.Builder#host(java.lang.String)" + } + ] + } + ] + } +] +[ + { + "module": "io.helidon.microprofile.openapi", + "types": [ + { + "annotatedType": "io.helidon.microprofile.openapi.MpOpenApiManagerConfig", + "type": "io.helidon.microprofile.openapi.MpOpenApiManagerConfig", + "producers": [ + "io.helidon.microprofile.openapi.MpOpenApiManagerConfig#create(io.helidon.common.config.Config)", + "io.helidon.microprofile.openapi.MpOpenApiManagerConfig#builder()" + ], + "options": [ + { + "description": "If `true` and the `jakarta.ws.rs.core.Application` class returns a non-empty set, endpoints defined by\n other resources are not included in the OpenAPI document.\n\n @return `true` if enabled, `false` otherwise", + "key": "mp.openapi.extensions.helidon.use-jaxrs-semantics", + "method": "io.helidon.microprofile.openapi.MpOpenApiManagerConfig.Builder#useJaxRsSemantics(boolean)", + "type": "java.lang.Boolean" + } + ] + } + ] + } +] diff --git a/all/pom.xml b/all/pom.xml index d949614f078..58d2d35cba8 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom helidon-all diff --git a/applications/mp/pom.xml b/applications/mp/pom.xml index 309ce06da94..cece884cb9c 100644 --- a/applications/mp/pom.xml +++ b/applications/mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-applications - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../parent/pom.xml helidon-mp diff --git a/applications/parent/pom.xml b/applications/parent/pom.xml index 2369af37320..9dc148d6fee 100644 --- a/applications/parent/pom.xml +++ b/applications/parent/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-applications-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml io.helidon.applications diff --git a/applications/pom.xml b/applications/pom.xml index 969de23879b..e8a58ec9eb1 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-dependencies - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../dependencies/pom.xml io.helidon.applications diff --git a/applications/se/pom.xml b/applications/se/pom.xml index 5c550a6a5b0..e3fc3792428 100644 --- a/applications/se/pom.xml +++ b/applications/se/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-applications - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../parent/pom.xml helidon-se diff --git a/archetypes/archetypes/pom.xml b/archetypes/archetypes/pom.xml index 70584e8d2b4..99d392d90e2 100644 --- a/archetypes/archetypes/pom.xml +++ b/archetypes/archetypes/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-archetype helidon-archetypes diff --git a/archetypes/legacy/bare-mp/pom.xml b/archetypes/legacy/bare-mp/pom.xml index dccd57f7dfe..76e5feb9ab0 100644 --- a/archetypes/legacy/bare-mp/pom.xml +++ b/archetypes/legacy/bare-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-legacy-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-bare-mp helidon-archetype diff --git a/archetypes/legacy/bare-se/pom.xml b/archetypes/legacy/bare-se/pom.xml index 5ebfa7b0e77..4163e47a536 100644 --- a/archetypes/legacy/bare-se/pom.xml +++ b/archetypes/legacy/bare-se/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-legacy-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-bare-se helidon-archetype diff --git a/archetypes/legacy/database-mp/pom.xml b/archetypes/legacy/database-mp/pom.xml index d1e70838933..f85c680b66c 100644 --- a/archetypes/legacy/database-mp/pom.xml +++ b/archetypes/legacy/database-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-legacy-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-database-mp helidon-archetype diff --git a/archetypes/legacy/database-se/pom.xml b/archetypes/legacy/database-se/pom.xml index b5bfee93dad..633636704a5 100644 --- a/archetypes/legacy/database-se/pom.xml +++ b/archetypes/legacy/database-se/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-legacy-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-database-se helidon-archetype diff --git a/archetypes/legacy/pom.xml b/archetypes/legacy/pom.xml index 08c84b2b6e1..0248323c497 100644 --- a/archetypes/legacy/pom.xml +++ b/archetypes/legacy/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-archetypes-legacy-project Helidon Legacy Archetypes diff --git a/archetypes/legacy/quickstart-mp/pom.xml b/archetypes/legacy/quickstart-mp/pom.xml index 715b432d477..f0b936918a4 100644 --- a/archetypes/legacy/quickstart-mp/pom.xml +++ b/archetypes/legacy/quickstart-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-legacy-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-quickstart-mp helidon-archetype diff --git a/archetypes/legacy/quickstart-se/pom.xml b/archetypes/legacy/quickstart-se/pom.xml index bd376e94425..8c7fe0d7398 100644 --- a/archetypes/legacy/quickstart-se/pom.xml +++ b/archetypes/legacy/quickstart-se/pom.xml @@ -23,7 +23,7 @@ io.helidon.archetypes helidon-archetypes-legacy-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-quickstart-se helidon-archetype diff --git a/archetypes/pom.xml b/archetypes/pom.xml index b6f35243bd5..1006c562d8a 100644 --- a/archetypes/pom.xml +++ b/archetypes/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.archetypes helidon-archetypes-project diff --git a/bom/pom.xml b/bom/pom.xml index 21811b1ba92..82b37cb2f22 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-parent - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../parent/pom.xml helidon-bom @@ -31,7 +31,7 @@ Helidon BOM POM - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT diff --git a/builder/api/pom.xml b/builder/api/pom.xml index 275d77eb21d..7af94469439 100644 --- a/builder/api/pom.xml +++ b/builder/api/pom.xml @@ -24,7 +24,7 @@ io.helidon.builder helidon-builder-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/codegen/pom.xml b/builder/codegen/pom.xml index fd808a5685d..d701cc9bd9b 100644 --- a/builder/codegen/pom.xml +++ b/builder/codegen/pom.xml @@ -23,7 +23,7 @@ io.helidon.builder helidon-builder-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/pom.xml b/builder/pom.xml index e217ac420b3..3d1322a6a87 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -25,7 +25,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/processor/pom.xml b/builder/processor/pom.xml index e12e10614f9..29675509222 100644 --- a/builder/processor/pom.xml +++ b/builder/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.builder helidon-builder-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/tests/builder/pom.xml b/builder/tests/builder/pom.xml index 0a1c918a941..29e3b09dac8 100644 --- a/builder/tests/builder/pom.xml +++ b/builder/tests/builder/pom.xml @@ -23,7 +23,7 @@ io.helidon.builder.tests helidon-builder-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/tests/codegen/pom.xml b/builder/tests/codegen/pom.xml index 6d0ae10cf9d..a53fee91f63 100644 --- a/builder/tests/codegen/pom.xml +++ b/builder/tests/codegen/pom.xml @@ -23,7 +23,7 @@ io.helidon.builder.tests helidon-builder-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/tests/common-types/pom.xml b/builder/tests/common-types/pom.xml index a5d35ebf675..1185096b64a 100644 --- a/builder/tests/common-types/pom.xml +++ b/builder/tests/common-types/pom.xml @@ -23,7 +23,7 @@ io.helidon.builder.tests helidon-builder-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/builder/tests/pom.xml b/builder/tests/pom.xml index c21c203f44c..8772249f6dc 100644 --- a/builder/tests/pom.xml +++ b/builder/tests/pom.xml @@ -25,7 +25,7 @@ io.helidon.builder helidon-builder-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/bundles/config/pom.xml b/bundles/config/pom.xml index 1cf2e83255d..78f110fa2df 100644 --- a/bundles/config/pom.xml +++ b/bundles/config/pom.xml @@ -22,7 +22,7 @@ io.helidon.bundles helidon-bundles-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-bundles-config diff --git a/bundles/pom.xml b/bundles/pom.xml index fb4acd5aacc..048f8e7a97b 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT config diff --git a/bundles/security/pom.xml b/bundles/security/pom.xml index 2e53e1292b6..a24bec5a558 100644 --- a/bundles/security/pom.xml +++ b/bundles/security/pom.xml @@ -22,7 +22,7 @@ io.helidon.bundles helidon-bundles-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/codegen/apt/pom.xml b/codegen/apt/pom.xml index f1777d9ae5e..f8e916dbdb4 100644 --- a/codegen/apt/pom.xml +++ b/codegen/apt/pom.xml @@ -22,7 +22,7 @@ io.helidon.codegen helidon-codegen-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-codegen-apt diff --git a/codegen/class-model/pom.xml b/codegen/class-model/pom.xml index 156adfaeefe..9c4eb7a3bdd 100644 --- a/codegen/class-model/pom.xml +++ b/codegen/class-model/pom.xml @@ -22,7 +22,7 @@ io.helidon.codegen helidon-codegen-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-codegen-class-model diff --git a/codegen/codegen/pom.xml b/codegen/codegen/pom.xml index d9669f37b95..bdff1303d4a 100644 --- a/codegen/codegen/pom.xml +++ b/codegen/codegen/pom.xml @@ -23,7 +23,7 @@ io.helidon.codegen helidon-codegen-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-codegen diff --git a/codegen/compiler/pom.xml b/codegen/compiler/pom.xml index 64c0fd07c3e..ad41538b9d4 100644 --- a/codegen/compiler/pom.xml +++ b/codegen/compiler/pom.xml @@ -22,7 +22,7 @@ io.helidon.codegen helidon-codegen-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-codegen-compiler diff --git a/codegen/helidon-copyright/pom.xml b/codegen/helidon-copyright/pom.xml index 75b072c9b03..440145b8732 100644 --- a/codegen/helidon-copyright/pom.xml +++ b/codegen/helidon-copyright/pom.xml @@ -23,7 +23,7 @@ io.helidon.codegen helidon-codegen-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/codegen/pom.xml b/codegen/pom.xml index bbc11b1087a..40dc6a70b9a 100644 --- a/codegen/pom.xml +++ b/codegen/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.codegen diff --git a/codegen/scan/pom.xml b/codegen/scan/pom.xml index b07b81135d5..ad049c8418e 100644 --- a/codegen/scan/pom.xml +++ b/codegen/scan/pom.xml @@ -22,7 +22,7 @@ io.helidon.codegen helidon-codegen-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-codegen-scan diff --git a/common/buffers/pom.xml b/common/buffers/pom.xml index 6689be7c1cb..8b6602c285e 100644 --- a/common/buffers/pom.xml +++ b/common/buffers/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-buffers Helidon Common Buffers diff --git a/common/common/pom.xml b/common/common/pom.xml index c415ee7680a..041db05b592 100644 --- a/common/common/pom.xml +++ b/common/common/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common Helidon Common diff --git a/common/config/pom.xml b/common/config/pom.xml index c957742ea6d..2347a0bdb91 100644 --- a/common/config/pom.xml +++ b/common/config/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-config Helidon Common Config diff --git a/common/configurable/pom.xml b/common/configurable/pom.xml index bbdfe29a263..fb860e83e30 100644 --- a/common/configurable/pom.xml +++ b/common/configurable/pom.xml @@ -24,7 +24,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT Helidon Common Configurable helidon-common-configurable diff --git a/common/context/pom.xml b/common/context/pom.xml index 92c7a45d6e7..fdff8e77215 100644 --- a/common/context/pom.xml +++ b/common/context/pom.xml @@ -22,7 +22,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-context Helidon Common Context diff --git a/common/crypto/pom.xml b/common/crypto/pom.xml index c602d1857bc..5828acfdb81 100644 --- a/common/crypto/pom.xml +++ b/common/crypto/pom.xml @@ -21,7 +21,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/common/features/api/pom.xml b/common/features/api/pom.xml index cc3bd1ee35d..8c4a3896753 100644 --- a/common/features/api/pom.xml +++ b/common/features/api/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.features helidon-common-features-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/common/features/features/pom.xml b/common/features/features/pom.xml index 6bef7ec0a03..c4c9e4183f0 100644 --- a/common/features/features/pom.xml +++ b/common/features/features/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.features helidon-common-features-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/common/features/pom.xml b/common/features/pom.xml index dd5a53b732a..e657f014e8a 100644 --- a/common/features/pom.xml +++ b/common/features/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/common/features/processor/pom.xml b/common/features/processor/pom.xml index 17724620068..d07dd99d820 100644 --- a/common/features/processor/pom.xml +++ b/common/features/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.features helidon-common-features-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-common-features-processor diff --git a/common/key-util/pom.xml b/common/key-util/pom.xml index 1374be04ab7..b2a994bea50 100644 --- a/common/key-util/pom.xml +++ b/common/key-util/pom.xml @@ -24,7 +24,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-key-util Helidon Common Key Util diff --git a/common/mapper/pom.xml b/common/mapper/pom.xml index 9340367305c..ea9309098cd 100644 --- a/common/mapper/pom.xml +++ b/common/mapper/pom.xml @@ -21,7 +21,7 @@ helidon-common-project io.helidon.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/common/media-type/pom.xml b/common/media-type/pom.xml index e36d3aaa582..da1b6ccfc4c 100644 --- a/common/media-type/pom.xml +++ b/common/media-type/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-media-type diff --git a/common/parameters/pom.xml b/common/parameters/pom.xml index 2abbc8de76f..18f9f5fc0d0 100644 --- a/common/parameters/pom.xml +++ b/common/parameters/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-parameters Helidon Common Parameters diff --git a/common/pom.xml b/common/pom.xml index 460ac203381..898d6543c98 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.common helidon-common-project diff --git a/common/processor/class-model/pom.xml b/common/processor/class-model/pom.xml index 5b33749c708..283594c5b4e 100644 --- a/common/processor/class-model/pom.xml +++ b/common/processor/class-model/pom.xml @@ -22,7 +22,7 @@ io.helidon.common.processor helidon-common-processor-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-processor-class-model diff --git a/common/processor/helidon-copyright/pom.xml b/common/processor/helidon-copyright/pom.xml index b6da86a516e..e4a4b932ac9 100644 --- a/common/processor/helidon-copyright/pom.xml +++ b/common/processor/helidon-copyright/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.processor helidon-common-processor-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/common/processor/pom.xml b/common/processor/pom.xml index 92345aae590..bd781a684ef 100644 --- a/common/processor/pom.xml +++ b/common/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/common/processor/processor/pom.xml b/common/processor/processor/pom.xml index 7d9e02257b3..e9e4ce53f2a 100644 --- a/common/processor/processor/pom.xml +++ b/common/processor/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.processor helidon-common-processor-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/common/reactive/pom.xml b/common/reactive/pom.xml index 945d980ab07..e97add8a485 100644 --- a/common/reactive/pom.xml +++ b/common/reactive/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-reactive Helidon Common Reactive diff --git a/common/security/pom.xml b/common/security/pom.xml index d5907402541..fe5853f43db 100644 --- a/common/security/pom.xml +++ b/common/security/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-security Helidon Common Security diff --git a/common/socket/pom.xml b/common/socket/pom.xml index d174ce0bde3..fd372809442 100644 --- a/common/socket/pom.xml +++ b/common/socket/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-socket Helidon Common Socket diff --git a/common/task/pom.xml b/common/task/pom.xml index 01713187561..d6d7d195029 100644 --- a/common/task/pom.xml +++ b/common/task/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-task Helidon Common Task diff --git a/common/testing/http-junit5/pom.xml b/common/testing/http-junit5/pom.xml index 4447bc73179..0eab7a8baee 100644 --- a/common/testing/http-junit5/pom.xml +++ b/common/testing/http-junit5/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.testing helidon-common-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-testing-http-junit5 diff --git a/common/testing/junit5/pom.xml b/common/testing/junit5/pom.xml index e0096627e1a..eb75fad7f76 100644 --- a/common/testing/junit5/pom.xml +++ b/common/testing/junit5/pom.xml @@ -23,7 +23,7 @@ io.helidon.common.testing helidon-common-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-testing-junit5 diff --git a/common/testing/pom.xml b/common/testing/pom.xml index 6f6db48a858..eba5d08485d 100644 --- a/common/testing/pom.xml +++ b/common/testing/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.common.testing diff --git a/common/tls/pom.xml b/common/tls/pom.xml index 7934038f4dd..783d81f50b7 100644 --- a/common/tls/pom.xml +++ b/common/tls/pom.xml @@ -24,7 +24,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-tls diff --git a/common/types/pom.xml b/common/types/pom.xml index ee0420cdb29..25b8cd1ddbb 100644 --- a/common/types/pom.xml +++ b/common/types/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-types diff --git a/common/uri/pom.xml b/common/uri/pom.xml index 54cfb4bb9cb..23a1dd61bf3 100644 --- a/common/uri/pom.xml +++ b/common/uri/pom.xml @@ -23,7 +23,7 @@ io.helidon.common helidon-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-common-uri Helidon Common URI diff --git a/config/config-mp/pom.xml b/config/config-mp/pom.xml index 3c7daedebe8..6eb598e3c5a 100644 --- a/config/config-mp/pom.xml +++ b/config/config-mp/pom.xml @@ -22,7 +22,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-mp diff --git a/config/config/pom.xml b/config/config/pom.xml index e788c3c70b2..374b0294508 100644 --- a/config/config/pom.xml +++ b/config/config/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config Helidon Config diff --git a/config/encryption/pom.xml b/config/encryption/pom.xml index 2125add17a7..6e6fb37e978 100644 --- a/config/encryption/pom.xml +++ b/config/encryption/pom.xml @@ -23,7 +23,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 helidon-config-encryption diff --git a/config/etcd/pom.xml b/config/etcd/pom.xml index 2e49ce6e2da..bf74074c3b9 100644 --- a/config/etcd/pom.xml +++ b/config/etcd/pom.xml @@ -23,7 +23,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-etcd Helidon Config Etcd diff --git a/config/git/pom.xml b/config/git/pom.xml index a6b297f97d0..6d853422666 100644 --- a/config/git/pom.xml +++ b/config/git/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-git Helidon Config Git diff --git a/config/hocon-mp/pom.xml b/config/hocon-mp/pom.xml index 15ca05314bc..4afccf75e47 100644 --- a/config/hocon-mp/pom.xml +++ b/config/hocon-mp/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-hocon-mp Helidon Config HOCON MP diff --git a/config/hocon/pom.xml b/config/hocon/pom.xml index 953f9fb2127..203ac5e7135 100644 --- a/config/hocon/pom.xml +++ b/config/hocon/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-hocon Helidon Config HOCON diff --git a/config/metadata-processor/pom.xml b/config/metadata-processor/pom.xml index a5f358f26db..66427109e6a 100644 --- a/config/metadata-processor/pom.xml +++ b/config/metadata-processor/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-metadata-processor Helidon Config Metadata Annotation Processor diff --git a/config/metadata/pom.xml b/config/metadata/pom.xml index 2b6ac8f8bdd..2c0a0eddc48 100644 --- a/config/metadata/pom.xml +++ b/config/metadata/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-metadata Helidon Config Metadata diff --git a/config/object-mapping/pom.xml b/config/object-mapping/pom.xml index 33fa607879c..bc6d6a13034 100644 --- a/config/object-mapping/pom.xml +++ b/config/object-mapping/pom.xml @@ -22,7 +22,7 @@ helidon-config-project io.helidon.config - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/config/pom.xml b/config/pom.xml index b71009011c7..eda2325f10b 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.config helidon-config-project diff --git a/config/testing/pom.xml b/config/testing/pom.xml index 4020b306c31..85f1c126f9a 100644 --- a/config/testing/pom.xml +++ b/config/testing/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-testing Helidon Config Testing diff --git a/config/tests/config-metadata-builder-api/pom.xml b/config/tests/config-metadata-builder-api/pom.xml index a1ffd5d69a8..ca53c2bbb61 100644 --- a/config/tests/config-metadata-builder-api/pom.xml +++ b/config/tests/config-metadata-builder-api/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-config-tests-config-metadata-builder-api diff --git a/config/tests/config-metadata-meta-api/pom.xml b/config/tests/config-metadata-meta-api/pom.xml index c3181ec8fe0..fd21d2d01b9 100644 --- a/config/tests/config-metadata-meta-api/pom.xml +++ b/config/tests/config-metadata-meta-api/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-config-tests-config-metadata-meta-api diff --git a/config/tests/integration-tests/pom.xml b/config/tests/integration-tests/pom.xml index 4bd16675c41..f6262cad1f7 100644 --- a/config/tests/integration-tests/pom.xml +++ b/config/tests/integration-tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-integration-tests Helidon Config Tests Integration diff --git a/config/tests/module-mappers-1-base/pom.xml b/config/tests/module-mappers-1-base/pom.xml index 9341e7f58b7..d5d53bd582c 100644 --- a/config/tests/module-mappers-1-base/pom.xml +++ b/config/tests/module-mappers-1-base/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-module-mappers-1-base Helidon Config Tests Mappers 1 diff --git a/config/tests/module-mappers-2-override/pom.xml b/config/tests/module-mappers-2-override/pom.xml index 3a70b9cea5c..e1657e603bd 100644 --- a/config/tests/module-mappers-2-override/pom.xml +++ b/config/tests/module-mappers-2-override/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-module-mappers-2-override Helidon Config Tests Parser 2 diff --git a/config/tests/module-meta-source-1/pom.xml b/config/tests/module-meta-source-1/pom.xml index fc9dc1fd2a2..aebbcd9e4be 100644 --- a/config/tests/module-meta-source-1/pom.xml +++ b/config/tests/module-meta-source-1/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-module-meta-source-1 Helidon Config Tests Meta Source 1 diff --git a/config/tests/module-meta-source-2/pom.xml b/config/tests/module-meta-source-2/pom.xml index fdcaf9bdb33..39105919760 100644 --- a/config/tests/module-meta-source-2/pom.xml +++ b/config/tests/module-meta-source-2/pom.xml @@ -25,7 +25,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-module-meta-source-2 Helidon Config Tests Meta Source 2 diff --git a/config/tests/module-parsers-1-override/pom.xml b/config/tests/module-parsers-1-override/pom.xml index 54bcdf53185..4157f6cdc50 100644 --- a/config/tests/module-parsers-1-override/pom.xml +++ b/config/tests/module-parsers-1-override/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-module-parsers-1-override Helidon Config Tests Parser 1 diff --git a/config/tests/pom.xml b/config/tests/pom.xml index f8bdb9beb64..28d007e3c1f 100644 --- a/config/tests/pom.xml +++ b/config/tests/pom.xml @@ -23,7 +23,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.config.tests helidon-config-tests-project diff --git a/config/tests/service-registry/pom.xml b/config/tests/service-registry/pom.xml index 09310504d0b..05062aa34d2 100644 --- a/config/tests/service-registry/pom.xml +++ b/config/tests/service-registry/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-service-registry Helidon Config Tests Service Registry diff --git a/config/tests/test-bundle/pom.xml b/config/tests/test-bundle/pom.xml index 211238b3c72..e4599c5cbdc 100644 --- a/config/tests/test-bundle/pom.xml +++ b/config/tests/test-bundle/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-bundle Helidon Config Tests Bundle diff --git a/config/tests/test-default_config-1-properties/pom.xml b/config/tests/test-default_config-1-properties/pom.xml index 9ca35a3437e..cc2ea411e84 100644 --- a/config/tests/test-default_config-1-properties/pom.xml +++ b/config/tests/test-default_config-1-properties/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-1-properties Helidon Config Tests Default Config 1 diff --git a/config/tests/test-default_config-2-hocon-json/pom.xml b/config/tests/test-default_config-2-hocon-json/pom.xml index 36453f96d51..87a6bb41fc9 100644 --- a/config/tests/test-default_config-2-hocon-json/pom.xml +++ b/config/tests/test-default_config-2-hocon-json/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-2-hocon-json Helidon Config Tests Default Config 2 diff --git a/config/tests/test-default_config-3-hocon/pom.xml b/config/tests/test-default_config-3-hocon/pom.xml index 993b07336b9..2944128250c 100644 --- a/config/tests/test-default_config-3-hocon/pom.xml +++ b/config/tests/test-default_config-3-hocon/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-3-hocon Helidon Config Tests Default Config 3 diff --git a/config/tests/test-default_config-4-yaml/pom.xml b/config/tests/test-default_config-4-yaml/pom.xml index 20460383e7b..f84ddbe7310 100644 --- a/config/tests/test-default_config-4-yaml/pom.xml +++ b/config/tests/test-default_config-4-yaml/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-4-yaml Helidon Config Tests Default Config 4 diff --git a/config/tests/test-default_config-5-env_vars/pom.xml b/config/tests/test-default_config-5-env_vars/pom.xml index c72eb9c9af4..85a739fc627 100644 --- a/config/tests/test-default_config-5-env_vars/pom.xml +++ b/config/tests/test-default_config-5-env_vars/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-5-env_vars Helidon Config Tests Default Config 5 diff --git a/config/tests/test-default_config-6-meta-properties/pom.xml b/config/tests/test-default_config-6-meta-properties/pom.xml index 35b2674b1e7..e7aebf81c59 100644 --- a/config/tests/test-default_config-6-meta-properties/pom.xml +++ b/config/tests/test-default_config-6-meta-properties/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-6-meta-properties Helidon Config Tests Default Config 6 diff --git a/config/tests/test-default_config-7-meta-hocon-json/pom.xml b/config/tests/test-default_config-7-meta-hocon-json/pom.xml index fc5b56ce552..9994c436966 100644 --- a/config/tests/test-default_config-7-meta-hocon-json/pom.xml +++ b/config/tests/test-default_config-7-meta-hocon-json/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-7-meta-hocon-json Helidon Config Tests Default Config 7 diff --git a/config/tests/test-default_config-8-meta-hocon/pom.xml b/config/tests/test-default_config-8-meta-hocon/pom.xml index 28b15066f74..3793915f2ef 100644 --- a/config/tests/test-default_config-8-meta-hocon/pom.xml +++ b/config/tests/test-default_config-8-meta-hocon/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-8-meta-hocon Helidon Config Tests Default Config 8 diff --git a/config/tests/test-default_config-9-meta-yaml/pom.xml b/config/tests/test-default_config-9-meta-yaml/pom.xml index 611b98be696..8753cfaca39 100644 --- a/config/tests/test-default_config-9-meta-yaml/pom.xml +++ b/config/tests/test-default_config-9-meta-yaml/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-default_config-9-meta-yaml Helidon Config Tests Default Config 9 diff --git a/config/tests/test-lazy-source/pom.xml b/config/tests/test-lazy-source/pom.xml index 323fd388ecf..a139483f0ff 100644 --- a/config/tests/test-lazy-source/pom.xml +++ b/config/tests/test-lazy-source/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-lazy-source Helidon Config Tests Lazy Source diff --git a/config/tests/test-mappers-1-common/pom.xml b/config/tests/test-mappers-1-common/pom.xml index 3af8b10cb41..7585f642b1b 100644 --- a/config/tests/test-mappers-1-common/pom.xml +++ b/config/tests/test-mappers-1-common/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-mappers-1-common Helidon Config Tests Mappers Common 1 diff --git a/config/tests/test-mappers-2-complex/pom.xml b/config/tests/test-mappers-2-complex/pom.xml index fbebae8a113..d2032c6d19b 100644 --- a/config/tests/test-mappers-2-complex/pom.xml +++ b/config/tests/test-mappers-2-complex/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-mappers-2-complex Helidon Config Tests Parsers 2 diff --git a/config/tests/test-meta-source/pom.xml b/config/tests/test-meta-source/pom.xml index 7a88701a915..f91fc8c6bc4 100644 --- a/config/tests/test-meta-source/pom.xml +++ b/config/tests/test-meta-source/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-meta-source Helidon Config Tests Meta Source diff --git a/config/tests/test-parsers-1-complex/pom.xml b/config/tests/test-parsers-1-complex/pom.xml index fe18d3a20f0..29e18fe2089 100644 --- a/config/tests/test-parsers-1-complex/pom.xml +++ b/config/tests/test-parsers-1-complex/pom.xml @@ -24,7 +24,7 @@ io.helidon.config.tests helidon-config-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-tests-test-parsers-1-complex Helidon Config Tests Parsers 1 diff --git a/config/yaml-mp/pom.xml b/config/yaml-mp/pom.xml index 67766045744..04ef717ba06 100644 --- a/config/yaml-mp/pom.xml +++ b/config/yaml-mp/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-yaml-mp Helidon Config YAML MP diff --git a/config/yaml/pom.xml b/config/yaml/pom.xml index 2c22459eba1..ffe145183a7 100644 --- a/config/yaml/pom.xml +++ b/config/yaml/pom.xml @@ -24,7 +24,7 @@ io.helidon.config helidon-config-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-config-yaml Helidon Config YAML diff --git a/cors/pom.xml b/cors/pom.xml index a4e59eba49d..8de7b3cd3dd 100644 --- a/cors/pom.xml +++ b/cors/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.cors diff --git a/dbclient/dbclient/pom.xml b/dbclient/dbclient/pom.xml index ae5a67939c0..b37feefe7f6 100644 --- a/dbclient/dbclient/pom.xml +++ b/dbclient/dbclient/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient diff --git a/dbclient/health/pom.xml b/dbclient/health/pom.xml index 1c5ccaf253b..232f779bd97 100644 --- a/dbclient/health/pom.xml +++ b/dbclient/health/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-health diff --git a/dbclient/hikari/pom.xml b/dbclient/hikari/pom.xml index a6f1ebff45e..ab226cf4bbe 100644 --- a/dbclient/hikari/pom.xml +++ b/dbclient/hikari/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-hikari diff --git a/dbclient/jdbc/pom.xml b/dbclient/jdbc/pom.xml index bc63ae4343e..0ba4f67975a 100644 --- a/dbclient/jdbc/pom.xml +++ b/dbclient/jdbc/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-jdbc diff --git a/dbclient/jsonp/pom.xml b/dbclient/jsonp/pom.xml index ca6740ab808..4eb4e455b92 100644 --- a/dbclient/jsonp/pom.xml +++ b/dbclient/jsonp/pom.xml @@ -21,7 +21,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/dbclient/metrics-hikari/pom.xml b/dbclient/metrics-hikari/pom.xml index ef7296e3bd3..4734cae619c 100644 --- a/dbclient/metrics-hikari/pom.xml +++ b/dbclient/metrics-hikari/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-metrics-hikari diff --git a/dbclient/metrics/pom.xml b/dbclient/metrics/pom.xml index 7d6ddca884d..5be2238432b 100644 --- a/dbclient/metrics/pom.xml +++ b/dbclient/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-metrics diff --git a/dbclient/mongodb/pom.xml b/dbclient/mongodb/pom.xml index c59f9783cc2..f73f90f5d9f 100644 --- a/dbclient/mongodb/pom.xml +++ b/dbclient/mongodb/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-mongodb diff --git a/dbclient/pom.xml b/dbclient/pom.xml index ecfbfb10a6d..f604c3e4510 100644 --- a/dbclient/pom.xml +++ b/dbclient/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/dbclient/tracing/pom.xml b/dbclient/tracing/pom.xml index 47f4a06e1af..2a27263ccb7 100644 --- a/dbclient/tracing/pom.xml +++ b/dbclient/tracing/pom.xml @@ -22,7 +22,7 @@ io.helidon.dbclient helidon-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-dbclient-tracing diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 546a528f132..c4b41226b91 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-bom - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../bom/pom.xml helidon-dependencies diff --git a/docs/pom.xml b/docs/pom.xml index 84ff31a98d7..5023ef83da4 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-docs site diff --git a/docs/src/main/asciidoc/includes/attributes.adoc b/docs/src/main/asciidoc/includes/attributes.adoc index 3a93fa520ec..3a2ce86d960 100644 --- a/docs/src/main/asciidoc/includes/attributes.adoc +++ b/docs/src/main/asciidoc/includes/attributes.adoc @@ -23,8 +23,8 @@ ifndef::attributes-included[] // functional attributes :imagesdir: {rootdir}/images -:helidon-version: 4.0.0-SNAPSHOT -:helidon-version-is-release: false +:helidon-version: 4.1.0-SNAPSHOT +:helidon-version-is-release: true :sourcedir: {rootdir}/../java/io/helidon/docs diff --git a/examples/config/basics/pom.xml b/examples/config/basics/pom.xml index 386666a3bd8..e44936582ce 100644 --- a/examples/config/basics/pom.xml +++ b/examples/config/basics/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/changes/pom.xml b/examples/config/changes/pom.xml index b3c98bf1641..44447b091a4 100644 --- a/examples/config/changes/pom.xml +++ b/examples/config/changes/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/git/pom.xml b/examples/config/git/pom.xml index 09ec02ad239..198d1c29888 100644 --- a/examples/config/git/pom.xml +++ b/examples/config/git/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/mapping/pom.xml b/examples/config/mapping/pom.xml index 9a74905ad5d..872618ac9d7 100644 --- a/examples/config/mapping/pom.xml +++ b/examples/config/mapping/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/metadata/pom.xml b/examples/config/metadata/pom.xml index f86c67c8cb9..832de3f97ad 100644 --- a/examples/config/metadata/pom.xml +++ b/examples/config/metadata/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/overrides/pom.xml b/examples/config/overrides/pom.xml index 58144039f34..3f302b264b9 100644 --- a/examples/config/overrides/pom.xml +++ b/examples/config/overrides/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/pom.xml b/examples/config/pom.xml index 121af129840..27a8ce96425 100644 --- a/examples/config/pom.xml +++ b/examples/config/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.config helidon-examples-config-project diff --git a/examples/config/profiles/pom.xml b/examples/config/profiles/pom.xml index 2c041e3f2ed..e3c60c19fbe 100644 --- a/examples/config/profiles/pom.xml +++ b/examples/config/profiles/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/config/sources/pom.xml b/examples/config/sources/pom.xml index 9e5324a8217..5ae02bd2861 100644 --- a/examples/config/sources/pom.xml +++ b/examples/config/sources/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.config diff --git a/examples/cors/pom.xml b/examples/cors/pom.xml index 764bc174650..6538f76832d 100644 --- a/examples/cors/pom.xml +++ b/examples/cors/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../applications/se/pom.xml io.helidon.examples diff --git a/examples/dbclient/common/pom.xml b/examples/dbclient/common/pom.xml index 72b10e5d0c0..8998b0ab496 100644 --- a/examples/dbclient/common/pom.xml +++ b/examples/dbclient/common/pom.xml @@ -22,7 +22,7 @@ io.helidon.examples.dbclient helidon-examples-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-examples-dbclient-common diff --git a/examples/dbclient/jdbc/pom.xml b/examples/dbclient/jdbc/pom.xml index c507293a418..147818d766b 100644 --- a/examples/dbclient/jdbc/pom.xml +++ b/examples/dbclient/jdbc/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/dbclient/mongodb/pom.xml b/examples/dbclient/mongodb/pom.xml index 3da3e02cad7..525b29db774 100644 --- a/examples/dbclient/mongodb/pom.xml +++ b/examples/dbclient/mongodb/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/dbclient/pokemons/pom.xml b/examples/dbclient/pokemons/pom.xml index 520fbe3facc..cd551b2fa1a 100644 --- a/examples/dbclient/pokemons/pom.xml +++ b/examples/dbclient/pokemons/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/dbclient/pom.xml b/examples/dbclient/pom.xml index fe463fc96b5..c6ba70766d3 100644 --- a/examples/dbclient/pom.xml +++ b/examples/dbclient/pom.xml @@ -22,7 +22,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/examples/employee-app/pom.xml b/examples/employee-app/pom.xml index 0b17c6968ba..4a109a3a49e 100644 --- a/examples/employee-app/pom.xml +++ b/examples/employee-app/pom.xml @@ -25,7 +25,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../applications/se/pom.xml io.helidon.examples.employee diff --git a/examples/graphql/basics/pom.xml b/examples/graphql/basics/pom.xml index 8cbdd744391..cbe70cd4bfb 100644 --- a/examples/graphql/basics/pom.xml +++ b/examples/graphql/basics/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.graphql diff --git a/examples/graphql/pom.xml b/examples/graphql/pom.xml index 5e16bebb6b6..ab8d15f8910 100644 --- a/examples/graphql/pom.xml +++ b/examples/graphql/pom.xml @@ -22,7 +22,7 @@ helidon-examples-project io.helidon.examples - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.graphql helidon-examples-graphql-project diff --git a/examples/health/basics/pom.xml b/examples/health/basics/pom.xml index a8316270ef1..1b67aeb121b 100644 --- a/examples/health/basics/pom.xml +++ b/examples/health/basics/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.health diff --git a/examples/health/pom.xml b/examples/health/pom.xml index 5f247d272d0..36cc2350fe3 100644 --- a/examples/health/pom.xml +++ b/examples/health/pom.xml @@ -22,7 +22,7 @@ helidon-examples-project io.helidon.examples - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.health helidon-examples-health-project diff --git a/examples/integrations/cdi/datasource-hikaricp-h2/pom.xml b/examples/integrations/cdi/datasource-hikaricp-h2/pom.xml index bf414756fcf..44df845d2cd 100644 --- a/examples/integrations/cdi/datasource-hikaricp-h2/pom.xml +++ b/examples/integrations/cdi/datasource-hikaricp-h2/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.integrations.cdi diff --git a/examples/integrations/cdi/datasource-hikaricp-mysql/pom.xml b/examples/integrations/cdi/datasource-hikaricp-mysql/pom.xml index 4de930eaada..4895618df1b 100644 --- a/examples/integrations/cdi/datasource-hikaricp-mysql/pom.xml +++ b/examples/integrations/cdi/datasource-hikaricp-mysql/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.integrations.cdi diff --git a/examples/integrations/cdi/datasource-hikaricp/pom.xml b/examples/integrations/cdi/datasource-hikaricp/pom.xml index 01b48eb0e68..8f90dfada21 100644 --- a/examples/integrations/cdi/datasource-hikaricp/pom.xml +++ b/examples/integrations/cdi/datasource-hikaricp/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.integrations.cdi diff --git a/examples/integrations/cdi/jpa/pom.xml b/examples/integrations/cdi/jpa/pom.xml index f4a8830471d..674456a4a8c 100644 --- a/examples/integrations/cdi/jpa/pom.xml +++ b/examples/integrations/cdi/jpa/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.integrations.cdi diff --git a/examples/integrations/cdi/pokemons/pom.xml b/examples/integrations/cdi/pokemons/pom.xml index 99149c35cdc..6a67a90fe12 100644 --- a/examples/integrations/cdi/pokemons/pom.xml +++ b/examples/integrations/cdi/pokemons/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.integrations.cdi diff --git a/examples/integrations/cdi/pom.xml b/examples/integrations/cdi/pom.xml index 28cb9ff3c9d..abbce000c57 100644 --- a/examples/integrations/cdi/pom.xml +++ b/examples/integrations/cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples.integrations helidon-examples-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.integrations.cdi helidon-examples-integrations-cdi-project diff --git a/examples/integrations/micrometer/mp/pom.xml b/examples/integrations/micrometer/mp/pom.xml index 8149cd497cb..9f90e019622 100644 --- a/examples/integrations/micrometer/mp/pom.xml +++ b/examples/integrations/micrometer/mp/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml 4.0.0 diff --git a/examples/integrations/micrometer/pom.xml b/examples/integrations/micrometer/pom.xml index 06f122ee11d..1eb8e97ff52 100644 --- a/examples/integrations/micrometer/pom.xml +++ b/examples/integrations/micrometer/pom.xml @@ -22,7 +22,7 @@ io.helidon.examples.integrations helidon-examples-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-examples-micrometer-project diff --git a/examples/integrations/micrometer/se/pom.xml b/examples/integrations/micrometer/se/pom.xml index 513a720fa81..8a7f00fb26c 100644 --- a/examples/integrations/micrometer/se/pom.xml +++ b/examples/integrations/micrometer/se/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/micronaut/data/pom.xml b/examples/integrations/micronaut/data/pom.xml index d997bc540b7..443dd344205 100644 --- a/examples/integrations/micronaut/data/pom.xml +++ b/examples/integrations/micronaut/data/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml helidon-examples-integrations-micronaut-data diff --git a/examples/integrations/micronaut/pom.xml b/examples/integrations/micronaut/pom.xml index 25aa810220c..807a130ea0d 100644 --- a/examples/integrations/micronaut/pom.xml +++ b/examples/integrations/micronaut/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples.integrations helidon-examples-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.integrations.micronaut helidon-examples-integrations-micronaut-project diff --git a/examples/integrations/microstream/greetings-mp/pom.xml b/examples/integrations/microstream/greetings-mp/pom.xml index f8406c2d1ef..41f9b477a75 100644 --- a/examples/integrations/microstream/greetings-mp/pom.xml +++ b/examples/integrations/microstream/greetings-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml diff --git a/examples/integrations/microstream/greetings-se/pom.xml b/examples/integrations/microstream/greetings-se/pom.xml index 89405eb2687..5ecd85e20a9 100644 --- a/examples/integrations/microstream/greetings-se/pom.xml +++ b/examples/integrations/microstream/greetings-se/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/microstream/pom.xml b/examples/integrations/microstream/pom.xml index e50976c784e..8f3ab5b3625 100644 --- a/examples/integrations/microstream/pom.xml +++ b/examples/integrations/microstream/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples.integrations helidon-examples-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.integrations.microstream diff --git a/examples/integrations/neo4j/pom.xml b/examples/integrations/neo4j/pom.xml index e6c25ce5d5e..f6ef7be7819 100644 --- a/examples/integrations/neo4j/pom.xml +++ b/examples/integrations/neo4j/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml helidon-examples-integration-neo4j diff --git a/examples/integrations/oci/atp-cdi/pom.xml b/examples/integrations/oci/atp-cdi/pom.xml index 75ca6aac3bb..f5245d6beb9 100644 --- a/examples/integrations/oci/atp-cdi/pom.xml +++ b/examples/integrations/oci/atp-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml diff --git a/examples/integrations/oci/atp/pom.xml b/examples/integrations/oci/atp/pom.xml index 4986b070cef..5e1f3985d76 100644 --- a/examples/integrations/oci/atp/pom.xml +++ b/examples/integrations/oci/atp/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/oci/metrics/pom.xml b/examples/integrations/oci/metrics/pom.xml index 496394f868e..f3a488af9eb 100644 --- a/examples/integrations/oci/metrics/pom.xml +++ b/examples/integrations/oci/metrics/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/oci/objectstorage-cdi/pom.xml b/examples/integrations/oci/objectstorage-cdi/pom.xml index eb5c3b4af30..4e1d155d4da 100644 --- a/examples/integrations/oci/objectstorage-cdi/pom.xml +++ b/examples/integrations/oci/objectstorage-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml diff --git a/examples/integrations/oci/objectstorage/pom.xml b/examples/integrations/oci/objectstorage/pom.xml index d6b03434e18..e6de12c28c9 100644 --- a/examples/integrations/oci/objectstorage/pom.xml +++ b/examples/integrations/oci/objectstorage/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/oci/pom.xml b/examples/integrations/oci/pom.xml index fdbb9489b65..abb77995f2f 100644 --- a/examples/integrations/oci/pom.xml +++ b/examples/integrations/oci/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples.integrations helidon-examples-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.integrations.oci helidon-examples-integrations-oci-project diff --git a/examples/integrations/oci/vault-cdi/pom.xml b/examples/integrations/oci/vault-cdi/pom.xml index a16dba0eb2c..7c89bf85a76 100644 --- a/examples/integrations/oci/vault-cdi/pom.xml +++ b/examples/integrations/oci/vault-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml diff --git a/examples/integrations/oci/vault/pom.xml b/examples/integrations/oci/vault/pom.xml index 3e931a2b011..ef97c7aa19f 100644 --- a/examples/integrations/oci/vault/pom.xml +++ b/examples/integrations/oci/vault/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/pom.xml b/examples/integrations/pom.xml index abead6217ee..0c190aeff27 100644 --- a/examples/integrations/pom.xml +++ b/examples/integrations/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.integrations helidon-examples-integrations-project diff --git a/examples/integrations/vault/hcp-cdi/pom.xml b/examples/integrations/vault/hcp-cdi/pom.xml index 02813df2967..3c73bd5344d 100644 --- a/examples/integrations/vault/hcp-cdi/pom.xml +++ b/examples/integrations/vault/hcp-cdi/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml diff --git a/examples/integrations/vault/hcp/pom.xml b/examples/integrations/vault/hcp/pom.xml index 207da58e590..f810dcf76a4 100644 --- a/examples/integrations/vault/hcp/pom.xml +++ b/examples/integrations/vault/hcp/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml diff --git a/examples/integrations/vault/pom.xml b/examples/integrations/vault/pom.xml index 30ba67027f9..632cb816475 100644 --- a/examples/integrations/vault/pom.xml +++ b/examples/integrations/vault/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples.integrations helidon-examples-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.integrations.vault helidon-examples-integrations-vault-project diff --git a/examples/jbatch/pom.xml b/examples/jbatch/pom.xml index 053f6645874..236d5255d2f 100644 --- a/examples/jbatch/pom.xml +++ b/examples/jbatch/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../applications/mp/pom.xml io.helidon.examples.jbatch diff --git a/examples/logging/jul/pom.xml b/examples/logging/jul/pom.xml index 7be6c0dfb4b..57877d18907 100644 --- a/examples/logging/jul/pom.xml +++ b/examples/logging/jul/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.logging diff --git a/examples/logging/log4j/pom.xml b/examples/logging/log4j/pom.xml index a5d892d084e..70efd046428 100644 --- a/examples/logging/log4j/pom.xml +++ b/examples/logging/log4j/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.logging diff --git a/examples/logging/logback-aot/pom.xml b/examples/logging/logback-aot/pom.xml index 514d7fa8d17..7b85fea9c4f 100644 --- a/examples/logging/logback-aot/pom.xml +++ b/examples/logging/logback-aot/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.logging diff --git a/examples/logging/pom.xml b/examples/logging/pom.xml index a2d62d891aa..7a2d1be6c44 100644 --- a/examples/logging/pom.xml +++ b/examples/logging/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.logging helidon-examples-logging-project diff --git a/examples/logging/slf4j/pom.xml b/examples/logging/slf4j/pom.xml index 56c8bcbc4a1..1693c199980 100644 --- a/examples/logging/slf4j/pom.xml +++ b/examples/logging/slf4j/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.logging diff --git a/examples/media/multipart/pom.xml b/examples/media/multipart/pom.xml index a5a94f612f4..cc2d47a85bf 100644 --- a/examples/media/multipart/pom.xml +++ b/examples/media/multipart/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.media diff --git a/examples/media/pom.xml b/examples/media/pom.xml index ba7954121d0..544752e31e8 100644 --- a/examples/media/pom.xml +++ b/examples/media/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.media helidon-examples-media-project diff --git a/examples/messaging/jms-websocket-mp/pom.xml b/examples/messaging/jms-websocket-mp/pom.xml index 4cd0325df0a..f5154e052db 100644 --- a/examples/messaging/jms-websocket-mp/pom.xml +++ b/examples/messaging/jms-websocket-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.jms diff --git a/examples/messaging/jms-websocket-se/pom.xml b/examples/messaging/jms-websocket-se/pom.xml index 8147216a2b1..6f59532055e 100644 --- a/examples/messaging/jms-websocket-se/pom.xml +++ b/examples/messaging/jms-websocket-se/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.jms diff --git a/examples/messaging/kafka-websocket-mp/pom.xml b/examples/messaging/kafka-websocket-mp/pom.xml index a17c1101bf4..603896f410c 100644 --- a/examples/messaging/kafka-websocket-mp/pom.xml +++ b/examples/messaging/kafka-websocket-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.messaging.mp diff --git a/examples/messaging/kafka-websocket-se/pom.xml b/examples/messaging/kafka-websocket-se/pom.xml index f8221a2ed6c..a55524de099 100644 --- a/examples/messaging/kafka-websocket-se/pom.xml +++ b/examples/messaging/kafka-websocket-se/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.messaging.se diff --git a/examples/messaging/oracle-aq-websocket-mp/pom.xml b/examples/messaging/oracle-aq-websocket-mp/pom.xml index ae59445aa07..f7d4f8f8e82 100644 --- a/examples/messaging/oracle-aq-websocket-mp/pom.xml +++ b/examples/messaging/oracle-aq-websocket-mp/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.messaging.aq diff --git a/examples/messaging/pom.xml b/examples/messaging/pom.xml index 732b9ebf58d..56a44e359a2 100644 --- a/examples/messaging/pom.xml +++ b/examples/messaging/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.messaging helidon-examples-messaging-project diff --git a/examples/messaging/weblogic-jms-mp/pom.xml b/examples/messaging/weblogic-jms-mp/pom.xml index 55111aa85ea..8b8fa2f3c0e 100644 --- a/examples/messaging/weblogic-jms-mp/pom.xml +++ b/examples/messaging/weblogic-jms-mp/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.messaging.wls diff --git a/examples/metrics/exemplar/pom.xml b/examples/metrics/exemplar/pom.xml index 9cd812898f3..94110c96bfa 100644 --- a/examples/metrics/exemplar/pom.xml +++ b/examples/metrics/exemplar/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml 4.0.0 diff --git a/examples/metrics/filtering/mp/pom.xml b/examples/metrics/filtering/mp/pom.xml index 76f9027b83c..0cc67b6df4f 100644 --- a/examples/metrics/filtering/mp/pom.xml +++ b/examples/metrics/filtering/mp/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml 4.0.0 diff --git a/examples/metrics/filtering/pom.xml b/examples/metrics/filtering/pom.xml index cfa8952f20c..a9b2ca4eaa5 100644 --- a/examples/metrics/filtering/pom.xml +++ b/examples/metrics/filtering/pom.xml @@ -23,7 +23,7 @@ helidon-examples-metrics-project io.helidon.examples - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/examples/metrics/filtering/se/pom.xml b/examples/metrics/filtering/se/pom.xml index 1eb06cce4f1..14e6d683f0f 100644 --- a/examples/metrics/filtering/se/pom.xml +++ b/examples/metrics/filtering/se/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml 4.0.0 diff --git a/examples/metrics/http-status-count-se/pom.xml b/examples/metrics/http-status-count-se/pom.xml index 864fe80efac..c32e3f075ab 100644 --- a/examples/metrics/http-status-count-se/pom.xml +++ b/examples/metrics/http-status-count-se/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples diff --git a/examples/metrics/kpi/pom.xml b/examples/metrics/kpi/pom.xml index 324d4648baa..f32ee3a7805 100644 --- a/examples/metrics/kpi/pom.xml +++ b/examples/metrics/kpi/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml 4.0.0 diff --git a/examples/metrics/pom.xml b/examples/metrics/pom.xml index fced6952dae..2ee63177b59 100644 --- a/examples/metrics/pom.xml +++ b/examples/metrics/pom.xml @@ -23,7 +23,7 @@ helidon-examples-project io.helidon.examples - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/examples/microprofile/bean-validation/pom.xml b/examples/microprofile/bean-validation/pom.xml index a81e37a10b6..cd42f002689 100644 --- a/examples/microprofile/bean-validation/pom.xml +++ b/examples/microprofile/bean-validation/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/cors/pom.xml b/examples/microprofile/cors/pom.xml index 66402679c31..468382208c1 100644 --- a/examples/microprofile/cors/pom.xml +++ b/examples/microprofile/cors/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/graphql/pom.xml b/examples/microprofile/graphql/pom.xml index 72a57f3380f..8477e99694a 100644 --- a/examples/microprofile/graphql/pom.xml +++ b/examples/microprofile/graphql/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/hello-world-explicit/pom.xml b/examples/microprofile/hello-world-explicit/pom.xml index cb6e934428a..5fc855dd99c 100644 --- a/examples/microprofile/hello-world-explicit/pom.xml +++ b/examples/microprofile/hello-world-explicit/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml helidon-examples-microprofile-hello-world-explicit diff --git a/examples/microprofile/hello-world-implicit/pom.xml b/examples/microprofile/hello-world-implicit/pom.xml index 60b20c36620..73d7ee4b01a 100644 --- a/examples/microprofile/hello-world-implicit/pom.xml +++ b/examples/microprofile/hello-world-implicit/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/http-status-count-mp/pom.xml b/examples/microprofile/http-status-count-mp/pom.xml index b60cd35a411..85808447d49 100644 --- a/examples/microprofile/http-status-count-mp/pom.xml +++ b/examples/microprofile/http-status-count-mp/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples diff --git a/examples/microprofile/idcs/pom.xml b/examples/microprofile/idcs/pom.xml index 9da98c02c8b..6b4fb1982cf 100644 --- a/examples/microprofile/idcs/pom.xml +++ b/examples/microprofile/idcs/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/lra/pom.xml b/examples/microprofile/lra/pom.xml index 0b771792e57..3c34f49cc29 100644 --- a/examples/microprofile/lra/pom.xml +++ b/examples/microprofile/lra/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/messaging-sse/pom.xml b/examples/microprofile/messaging-sse/pom.xml index 8f9bd627c44..f3fe7809a59 100644 --- a/examples/microprofile/messaging-sse/pom.xml +++ b/examples/microprofile/messaging-sse/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/multipart/pom.xml b/examples/microprofile/multipart/pom.xml index 6d11cb71f52..dbc1e2f25ed 100644 --- a/examples/microprofile/multipart/pom.xml +++ b/examples/microprofile/multipart/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/multiport/pom.xml b/examples/microprofile/multiport/pom.xml index 76637088362..c2de80db069 100644 --- a/examples/microprofile/multiport/pom.xml +++ b/examples/microprofile/multiport/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/oci-tls-certificates/pom.xml b/examples/microprofile/oci-tls-certificates/pom.xml index 6218d284b2e..4f99f82a0bb 100644 --- a/examples/microprofile/oci-tls-certificates/pom.xml +++ b/examples/microprofile/oci-tls-certificates/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml diff --git a/examples/microprofile/oidc/pom.xml b/examples/microprofile/oidc/pom.xml index 3f38540c624..13094c3ea71 100644 --- a/examples/microprofile/oidc/pom.xml +++ b/examples/microprofile/oidc/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/openapi/pom.xml b/examples/microprofile/openapi/pom.xml index 685ee5ef3bc..14cb9754b24 100644 --- a/examples/microprofile/openapi/pom.xml +++ b/examples/microprofile/openapi/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/pom.xml b/examples/microprofile/pom.xml index f1258b6a212..dfb42e7b5eb 100644 --- a/examples/microprofile/pom.xml +++ b/examples/microprofile/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.examples.microprofile diff --git a/examples/microprofile/security/pom.xml b/examples/microprofile/security/pom.xml index 6c880a2d7e2..a08e65d9f87 100644 --- a/examples/microprofile/security/pom.xml +++ b/examples/microprofile/security/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/static-content/pom.xml b/examples/microprofile/static-content/pom.xml index fcb57157ff1..a5cf009b6ed 100644 --- a/examples/microprofile/static-content/pom.xml +++ b/examples/microprofile/static-content/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/telemetry/greeting/pom.xml b/examples/microprofile/telemetry/greeting/pom.xml index 9d526850ad0..3985c239da6 100644 --- a/examples/microprofile/telemetry/greeting/pom.xml +++ b/examples/microprofile/telemetry/greeting/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/telemetry/pom.xml b/examples/microprofile/telemetry/pom.xml index 17d4a04c51b..e44c5ef2ac7 100644 --- a/examples/microprofile/telemetry/pom.xml +++ b/examples/microprofile/telemetry/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples.microprofile helidon-examples-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.telemetry helidon-examples-microprofile-telemetry diff --git a/examples/microprofile/telemetry/secondary/pom.xml b/examples/microprofile/telemetry/secondary/pom.xml index d8e5f0c1f81..68a4fe5569c 100644 --- a/examples/microprofile/telemetry/secondary/pom.xml +++ b/examples/microprofile/telemetry/secondary/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/microprofile/tls/pom.xml b/examples/microprofile/tls/pom.xml index 29298d6bd01..8a25029a4cb 100644 --- a/examples/microprofile/tls/pom.xml +++ b/examples/microprofile/tls/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml diff --git a/examples/microprofile/websocket/pom.xml b/examples/microprofile/websocket/pom.xml index d3ee243520e..852c2b6fcb1 100644 --- a/examples/microprofile/websocket/pom.xml +++ b/examples/microprofile/websocket/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.microprofile diff --git a/examples/openapi-tools/pom.xml b/examples/openapi-tools/pom.xml index 59718e4f03d..a016026446a 100644 --- a/examples/openapi-tools/pom.xml +++ b/examples/openapi-tools/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.examples.openapi.tools diff --git a/examples/openapi/pom.xml b/examples/openapi/pom.xml index d88cedded7f..3003bf0c4b0 100644 --- a/examples/openapi/pom.xml +++ b/examples/openapi/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../applications/se/pom.xml io.helidon.examples diff --git a/examples/pom.xml b/examples/pom.xml index ac4c94ea6f6..58b5b6f68cd 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples helidon-examples-project diff --git a/examples/quickstarts/helidon-quickstart-mp/build.gradle b/examples/quickstarts/helidon-quickstart-mp/build.gradle index d2b19cc1336..ea6f662ebad 100644 --- a/examples/quickstarts/helidon-quickstart-mp/build.gradle +++ b/examples/quickstarts/helidon-quickstart-mp/build.gradle @@ -32,7 +32,7 @@ tasks.withType(JavaCompile) { } ext { - helidonversion = '4.0.0-SNAPSHOT' + helidonversion = '4.1.0-SNAPSHOT' mainClass='io.helidon.microprofile.cdi.Main' } diff --git a/examples/quickstarts/helidon-quickstart-mp/pom.xml b/examples/quickstarts/helidon-quickstart-mp/pom.xml index 2abc9acd050..1e5442e9f60 100644 --- a/examples/quickstarts/helidon-quickstart-mp/pom.xml +++ b/examples/quickstarts/helidon-quickstart-mp/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.quickstarts diff --git a/examples/quickstarts/helidon-quickstart-se/build.gradle b/examples/quickstarts/helidon-quickstart-se/build.gradle index 8ad817521ec..5d1f00951fb 100644 --- a/examples/quickstarts/helidon-quickstart-se/build.gradle +++ b/examples/quickstarts/helidon-quickstart-se/build.gradle @@ -31,7 +31,7 @@ tasks.withType(JavaCompile) { } ext { - helidonversion = '4.0.0-SNAPSHOT' + helidonversion = '4.1.0-SNAPSHOT' mainClass='io.helidon.examples.quickstart.se.Main' } diff --git a/examples/quickstarts/helidon-quickstart-se/pom.xml b/examples/quickstarts/helidon-quickstart-se/pom.xml index b8b2fc473a7..35568184eca 100644 --- a/examples/quickstarts/helidon-quickstart-se/pom.xml +++ b/examples/quickstarts/helidon-quickstart-se/pom.xml @@ -24,12 +24,12 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.quickstarts helidon-quickstart-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT Helidon Examples Quickstart SE diff --git a/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml b/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml index c7972e34ecd..8ce2725c387 100644 --- a/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml +++ b/examples/quickstarts/helidon-standalone-quickstart-mp/pom.xml @@ -22,11 +22,11 @@ 4.0.0 io.helidon.examples.quickstarts helidon-standalone-quickstart-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT Helidon Examples Standalone Quickstart MP - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.cdi.Main 21 diff --git a/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml b/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml index 4c3fae982da..3c7508ef397 100644 --- a/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml +++ b/examples/quickstarts/helidon-standalone-quickstart-se/pom.xml @@ -23,11 +23,11 @@ 4.0.0 io.helidon.examples.quickstarts helidon-standalone-quickstart-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT Helidon Examples Standalone Quickstart SE - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT test io.helidon.examples.quickstart.se.Main diff --git a/examples/quickstarts/pom.xml b/examples/quickstarts/pom.xml index f1feb7d189c..662f8b0c0c0 100644 --- a/examples/quickstarts/pom.xml +++ b/examples/quickstarts/pom.xml @@ -23,7 +23,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.quickstarts examples-quickstarts-project diff --git a/examples/security/attribute-based-access-control/pom.xml b/examples/security/attribute-based-access-control/pom.xml index ce79d2347ea..71b7e1b886e 100644 --- a/examples/security/attribute-based-access-control/pom.xml +++ b/examples/security/attribute-based-access-control/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.security diff --git a/examples/security/basic-auth-with-static-content/pom.xml b/examples/security/basic-auth-with-static-content/pom.xml index ad4671142c0..cd6eae80d0e 100644 --- a/examples/security/basic-auth-with-static-content/pom.xml +++ b/examples/security/basic-auth-with-static-content/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/google-login/pom.xml b/examples/security/google-login/pom.xml index 0d471c5e93b..4b109f17691 100644 --- a/examples/security/google-login/pom.xml +++ b/examples/security/google-login/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/idcs-login/pom.xml b/examples/security/idcs-login/pom.xml index 09188ee7b18..c98380f7a30 100644 --- a/examples/security/idcs-login/pom.xml +++ b/examples/security/idcs-login/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.security diff --git a/examples/security/outbound-override/pom.xml b/examples/security/outbound-override/pom.xml index a6f3589cbb0..5390fe1f365 100644 --- a/examples/security/outbound-override/pom.xml +++ b/examples/security/outbound-override/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/pom.xml b/examples/security/pom.xml index 4ac87a26621..f86234ab009 100644 --- a/examples/security/pom.xml +++ b/examples/security/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.security helidon-examples-security-project diff --git a/examples/security/programmatic/pom.xml b/examples/security/programmatic/pom.xml index 5564336d2b5..11e162583c8 100644 --- a/examples/security/programmatic/pom.xml +++ b/examples/security/programmatic/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/spi-examples/pom.xml b/examples/security/spi-examples/pom.xml index 678294c3525..4d4f5495a16 100644 --- a/examples/security/spi-examples/pom.xml +++ b/examples/security/spi-examples/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/vaults/pom.xml b/examples/security/vaults/pom.xml index 33f2a6841b6..0590c02b0ec 100644 --- a/examples/security/vaults/pom.xml +++ b/examples/security/vaults/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/webserver-digest-auth/pom.xml b/examples/security/webserver-digest-auth/pom.xml index f4bb105c7ef..de6656bb48f 100644 --- a/examples/security/webserver-digest-auth/pom.xml +++ b/examples/security/webserver-digest-auth/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/security/webserver-signatures/pom.xml b/examples/security/webserver-signatures/pom.xml index 348d8cb6ca8..d8adea65c95 100644 --- a/examples/security/webserver-signatures/pom.xml +++ b/examples/security/webserver-signatures/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.security diff --git a/examples/todo-app/backend/pom.xml b/examples/todo-app/backend/pom.xml index 5c95a2453a0..315aac05604 100644 --- a/examples/todo-app/backend/pom.xml +++ b/examples/todo-app/backend/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.examples.todos diff --git a/examples/todo-app/frontend/pom.xml b/examples/todo-app/frontend/pom.xml index 70c1c41fbcf..4cacc61aa4c 100644 --- a/examples/todo-app/frontend/pom.xml +++ b/examples/todo-app/frontend/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.todo diff --git a/examples/todo-app/pom.xml b/examples/todo-app/pom.xml index 7f36558300c..f7391508a41 100644 --- a/examples/todo-app/pom.xml +++ b/examples/todo-app/pom.xml @@ -24,13 +24,13 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.todos example-todo-app-project pom - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT Helidon Examples TODO Demo diff --git a/examples/translator-app/backend/pom.xml b/examples/translator-app/backend/pom.xml index 585d0ce6656..33a21820683 100644 --- a/examples/translator-app/backend/pom.xml +++ b/examples/translator-app/backend/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.translator diff --git a/examples/translator-app/frontend/pom.xml b/examples/translator-app/frontend/pom.xml index d1511950d29..01434852475 100644 --- a/examples/translator-app/frontend/pom.xml +++ b/examples/translator-app/frontend/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.translator diff --git a/examples/translator-app/pom.xml b/examples/translator-app/pom.xml index 550e0138868..7e72d9efb94 100644 --- a/examples/translator-app/pom.xml +++ b/examples/translator-app/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.translator helidon-examples-translator-project diff --git a/examples/webclient/pom.xml b/examples/webclient/pom.xml index 999fa1f20ce..6010ac27811 100644 --- a/examples/webclient/pom.xml +++ b/examples/webclient/pom.xml @@ -22,7 +22,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.webclient diff --git a/examples/webclient/standalone/pom.xml b/examples/webclient/standalone/pom.xml index 145b70d8e22..5fbfa20c22e 100644 --- a/examples/webclient/standalone/pom.xml +++ b/examples/webclient/standalone/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/basic/pom.xml b/examples/webserver/basic/pom.xml index 042bd7e0fa1..a675ddb24a8 100644 --- a/examples/webserver/basic/pom.xml +++ b/examples/webserver/basic/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/basics/pom.xml b/examples/webserver/basics/pom.xml index ba5d61fc8ef..522a16be322 100644 --- a/examples/webserver/basics/pom.xml +++ b/examples/webserver/basics/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/comment-aas/pom.xml b/examples/webserver/comment-aas/pom.xml index 08efdd14bc3..b5a1c3b2c9a 100644 --- a/examples/webserver/comment-aas/pom.xml +++ b/examples/webserver/comment-aas/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/echo/pom.xml b/examples/webserver/echo/pom.xml index aa2c1c16b2b..9228084dd70 100644 --- a/examples/webserver/echo/pom.xml +++ b/examples/webserver/echo/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/fault-tolerance/pom.xml b/examples/webserver/fault-tolerance/pom.xml index f7fcae3eb59..7149794a0ea 100644 --- a/examples/webserver/fault-tolerance/pom.xml +++ b/examples/webserver/fault-tolerance/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/imperative/pom.xml b/examples/webserver/imperative/pom.xml index 5b29ac9431a..11a672c0edc 100644 --- a/examples/webserver/imperative/pom.xml +++ b/examples/webserver/imperative/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/multiport/pom.xml b/examples/webserver/multiport/pom.xml index 40419185599..9c6072db269 100644 --- a/examples/webserver/multiport/pom.xml +++ b/examples/webserver/multiport/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/mutual-tls/pom.xml b/examples/webserver/mutual-tls/pom.xml index f36193417c4..887d49836e2 100644 --- a/examples/webserver/mutual-tls/pom.xml +++ b/examples/webserver/mutual-tls/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/observe/pom.xml b/examples/webserver/observe/pom.xml index 622e3fb28fd..23e8e8f78ab 100644 --- a/examples/webserver/observe/pom.xml +++ b/examples/webserver/observe/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/opentracing/pom.xml b/examples/webserver/opentracing/pom.xml index 7165d693afb..5d2130acce3 100644 --- a/examples/webserver/opentracing/pom.xml +++ b/examples/webserver/opentracing/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/pom.xml b/examples/webserver/pom.xml index 2de3839cb88..0fc5cdfdc11 100644 --- a/examples/webserver/pom.xml +++ b/examples/webserver/pom.xml @@ -24,7 +24,7 @@ io.helidon.examples helidon-examples-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.webserver helidon-examples-webserver-project diff --git a/examples/webserver/protocols/pom.xml b/examples/webserver/protocols/pom.xml index 67bd3b2bd79..50633319a0e 100644 --- a/examples/webserver/protocols/pom.xml +++ b/examples/webserver/protocols/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/static-content/pom.xml b/examples/webserver/static-content/pom.xml index 5be600e354b..393cb342c7b 100644 --- a/examples/webserver/static-content/pom.xml +++ b/examples/webserver/static-content/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/streaming/pom.xml b/examples/webserver/streaming/pom.xml index d352915039c..a6069f4c441 100644 --- a/examples/webserver/streaming/pom.xml +++ b/examples/webserver/streaming/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/tls/pom.xml b/examples/webserver/tls/pom.xml index 121c50f5995..9ec5325a33b 100644 --- a/examples/webserver/tls/pom.xml +++ b/examples/webserver/tls/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/tracing/pom.xml b/examples/webserver/tracing/pom.xml index 16d65ff4cf3..5411695c29b 100644 --- a/examples/webserver/tracing/pom.xml +++ b/examples/webserver/tracing/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/examples/webserver/tutorial/pom.xml b/examples/webserver/tutorial/pom.xml index c41458a8240..0d5d7d9d89e 100644 --- a/examples/webserver/tutorial/pom.xml +++ b/examples/webserver/tutorial/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/examples/webserver/websocket/pom.xml b/examples/webserver/websocket/pom.xml index 5a337b2d4cd..f27badd14d8 100644 --- a/examples/webserver/websocket/pom.xml +++ b/examples/webserver/websocket/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver diff --git a/fault-tolerance/fault-tolerance/pom.xml b/fault-tolerance/fault-tolerance/pom.xml index 4e51b161d8e..56d8ffd184a 100644 --- a/fault-tolerance/fault-tolerance/pom.xml +++ b/fault-tolerance/fault-tolerance/pom.xml @@ -22,7 +22,7 @@ io.helidon.fault-tolerance helidon-fault-tolerance-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-fault-tolerance diff --git a/fault-tolerance/pom.xml b/fault-tolerance/pom.xml index 0c43b134ef7..a3b5e4cbcf3 100644 --- a/fault-tolerance/pom.xml +++ b/fault-tolerance/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.fault-tolerance helidon-fault-tolerance-project diff --git a/graphql/pom.xml b/graphql/pom.xml index a82a5aa32fd..a39dec3a30d 100644 --- a/graphql/pom.xml +++ b/graphql/pom.xml @@ -26,7 +26,7 @@ helidon-project io.helidon - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.graphql diff --git a/graphql/server/pom.xml b/graphql/server/pom.xml index 381935c099c..d2eb6d9dda4 100644 --- a/graphql/server/pom.xml +++ b/graphql/server/pom.xml @@ -25,7 +25,7 @@ io.helidon.graphql helidon-graphql-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-graphql-server diff --git a/health/health-checks/pom.xml b/health/health-checks/pom.xml index 533874b8740..6d03d787962 100644 --- a/health/health-checks/pom.xml +++ b/health/health-checks/pom.xml @@ -22,7 +22,7 @@ io.helidon.health helidon-health-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-health-checks diff --git a/health/health/pom.xml b/health/health/pom.xml index a7f90077163..8dd1497bcd1 100644 --- a/health/health/pom.xml +++ b/health/health/pom.xml @@ -21,7 +21,7 @@ helidon-health-project io.helidon.health - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/health/pom.xml b/health/pom.xml index 023d1ee9fe8..abcddd58648 100644 --- a/health/pom.xml +++ b/health/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/helidon/pom.xml b/helidon/pom.xml index 1f8fbef473b..3cd58d704e2 100644 --- a/helidon/pom.xml +++ b/helidon/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon diff --git a/http/encoding/deflate/pom.xml b/http/encoding/deflate/pom.xml index 746f9ac7094..baaea1372ef 100644 --- a/http/encoding/deflate/pom.xml +++ b/http/encoding/deflate/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.encoding helidon-http-encoding-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-encoding-deflate diff --git a/http/encoding/encoding/pom.xml b/http/encoding/encoding/pom.xml index 753fd9199a8..268542fb9ce 100644 --- a/http/encoding/encoding/pom.xml +++ b/http/encoding/encoding/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.encoding helidon-http-encoding-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-encoding diff --git a/http/encoding/gzip/pom.xml b/http/encoding/gzip/pom.xml index ef89356f22e..f8c4a55c09a 100644 --- a/http/encoding/gzip/pom.xml +++ b/http/encoding/gzip/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.encoding helidon-http-encoding-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-encoding-gzip diff --git a/http/encoding/pom.xml b/http/encoding/pom.xml index 109226b5631..e767147197f 100644 --- a/http/encoding/pom.xml +++ b/http/encoding/pom.xml @@ -21,7 +21,7 @@ io.helidon.http helidon-http-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.http.encoding diff --git a/http/http/pom.xml b/http/http/pom.xml index 9a2459f6f53..e013cf91f10 100644 --- a/http/http/pom.xml +++ b/http/http/pom.xml @@ -23,7 +23,7 @@ io.helidon.http helidon-http-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http diff --git a/http/http2/pom.xml b/http/http2/pom.xml index b4cf408f6d2..f2e19a94535 100644 --- a/http/http2/pom.xml +++ b/http/http2/pom.xml @@ -21,7 +21,7 @@ io.helidon.http helidon-http-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-http2 diff --git a/http/media/jackson/pom.xml b/http/media/jackson/pom.xml index 0ad8af7fbac..23580209ffd 100644 --- a/http/media/jackson/pom.xml +++ b/http/media/jackson/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.media helidon-http-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-media-jackson diff --git a/http/media/jsonb/pom.xml b/http/media/jsonb/pom.xml index 8fa89803478..1000e528905 100644 --- a/http/media/jsonb/pom.xml +++ b/http/media/jsonb/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.media helidon-http-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-media-jsonb diff --git a/http/media/jsonp/pom.xml b/http/media/jsonp/pom.xml index 4e37fe92e19..839d4c7d3e4 100644 --- a/http/media/jsonp/pom.xml +++ b/http/media/jsonp/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.media helidon-http-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-media-jsonp diff --git a/http/media/media/pom.xml b/http/media/media/pom.xml index 514ad9b8a06..8627bf155e1 100644 --- a/http/media/media/pom.xml +++ b/http/media/media/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.media helidon-http-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-media diff --git a/http/media/multipart/pom.xml b/http/media/multipart/pom.xml index 6f5f0b6a888..026fdf4e8e1 100644 --- a/http/media/multipart/pom.xml +++ b/http/media/multipart/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.media helidon-http-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-media-multipart diff --git a/http/media/pom.xml b/http/media/pom.xml index 3d6fdf89d1f..1d3d66d8ebc 100644 --- a/http/media/pom.xml +++ b/http/media/pom.xml @@ -21,7 +21,7 @@ io.helidon.http helidon-http-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.http.media diff --git a/http/pom.xml b/http/pom.xml index ce4ef69d1f3..2b878ecf8a0 100644 --- a/http/pom.xml +++ b/http/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.http diff --git a/http/sse/pom.xml b/http/sse/pom.xml index d55a54ea210..5c2e0e78493 100644 --- a/http/sse/pom.xml +++ b/http/sse/pom.xml @@ -21,7 +21,7 @@ io.helidon.http helidon-http-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-sse diff --git a/http/tests/encoding/deflate/pom.xml b/http/tests/encoding/deflate/pom.xml index 330a7cb0ba1..223b841c266 100644 --- a/http/tests/encoding/deflate/pom.xml +++ b/http/tests/encoding/deflate/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests.encoding helidon-http-tests-encoding-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-tests-integration-encoding-deflate diff --git a/http/tests/encoding/gzip/pom.xml b/http/tests/encoding/gzip/pom.xml index 60f2a24c106..e7d73b704b4 100644 --- a/http/tests/encoding/gzip/pom.xml +++ b/http/tests/encoding/gzip/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests.encoding helidon-http-tests-encoding-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-tests-encoding-gzip diff --git a/http/tests/encoding/pom.xml b/http/tests/encoding/pom.xml index 78f8acb1276..ed492830d8d 100644 --- a/http/tests/encoding/pom.xml +++ b/http/tests/encoding/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests helidon-http-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.http.tests.encoding diff --git a/http/tests/media/jsonb/pom.xml b/http/tests/media/jsonb/pom.xml index 064e14c919c..4d041afbb5d 100644 --- a/http/tests/media/jsonb/pom.xml +++ b/http/tests/media/jsonb/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests.media helidon-http-tests-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-tests-tegration-media-jsonb diff --git a/http/tests/media/jsonp/pom.xml b/http/tests/media/jsonp/pom.xml index 15d4f2f6b15..5c60f3bd9a6 100644 --- a/http/tests/media/jsonp/pom.xml +++ b/http/tests/media/jsonp/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests.media helidon-http-tests-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-tests-media-jsonp diff --git a/http/tests/media/multipart/pom.xml b/http/tests/media/multipart/pom.xml index 0f762f9ef16..aa96e980ded 100644 --- a/http/tests/media/multipart/pom.xml +++ b/http/tests/media/multipart/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests.media helidon-http-tests-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-tests-media-multipart diff --git a/http/tests/media/pom.xml b/http/tests/media/pom.xml index cc0a2739de5..5514e1dc659 100644 --- a/http/tests/media/pom.xml +++ b/http/tests/media/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests helidon-http-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.http.tests.media diff --git a/http/tests/media/string/pom.xml b/http/tests/media/string/pom.xml index 1d89bd09416..cbf67e3629f 100644 --- a/http/tests/media/string/pom.xml +++ b/http/tests/media/string/pom.xml @@ -21,7 +21,7 @@ io.helidon.http.tests.media helidon-http-tests-media-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-http-tests-media-string diff --git a/http/tests/pom.xml b/http/tests/pom.xml index 7d55d653d1b..2523e745903 100644 --- a/http/tests/pom.xml +++ b/http/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.http helidon-http-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.http.tests diff --git a/inject/api/pom.xml b/inject/api/pom.xml index 363fd687085..2a635e4a510 100644 --- a/inject/api/pom.xml +++ b/inject/api/pom.xml @@ -21,7 +21,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/api/pom.xml b/inject/configdriven/api/pom.xml index 160b0d9b8e3..1cef442d90c 100644 --- a/inject/configdriven/api/pom.xml +++ b/inject/configdriven/api/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.configdriven helidon-inject-configdriven-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/pom.xml b/inject/configdriven/pom.xml index 2f03938b0c3..101ceba4700 100644 --- a/inject/configdriven/pom.xml +++ b/inject/configdriven/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/processor/pom.xml b/inject/configdriven/processor/pom.xml index 1b3d9fc6eeb..97dbc72ce79 100644 --- a/inject/configdriven/processor/pom.xml +++ b/inject/configdriven/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.configdriven helidon-inject-configdriven-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/runtime/pom.xml b/inject/configdriven/runtime/pom.xml index f4be4ec75a7..7406f330e50 100644 --- a/inject/configdriven/runtime/pom.xml +++ b/inject/configdriven/runtime/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.configdriven helidon-inject-configdriven-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/tests/config/pom.xml b/inject/configdriven/tests/config/pom.xml index 8e542f74d64..a0763c48bfb 100644 --- a/inject/configdriven/tests/config/pom.xml +++ b/inject/configdriven/tests/config/pom.xml @@ -21,7 +21,7 @@ io.helidon.inject.configdriven.tests helidon-inject-configdriven-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/tests/configuredby-application/pom.xml b/inject/configdriven/tests/configuredby-application/pom.xml index a1c210bebc7..3b357011c75 100644 --- a/inject/configdriven/tests/configuredby-application/pom.xml +++ b/inject/configdriven/tests/configuredby-application/pom.xml @@ -21,7 +21,7 @@ io.helidon.inject.configdriven.tests helidon-inject-configdriven-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/tests/configuredby/pom.xml b/inject/configdriven/tests/configuredby/pom.xml index 70dccae0ce5..b29cb3d2754 100644 --- a/inject/configdriven/tests/configuredby/pom.xml +++ b/inject/configdriven/tests/configuredby/pom.xml @@ -21,7 +21,7 @@ io.helidon.inject.configdriven.tests helidon-inject-configdriven-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/configdriven/tests/pom.xml b/inject/configdriven/tests/pom.xml index b1fb154283e..db836ac14a7 100644 --- a/inject/configdriven/tests/pom.xml +++ b/inject/configdriven/tests/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.configdriven helidon-inject-configdriven-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/maven-plugin/pom.xml b/inject/maven-plugin/pom.xml index edf736aa955..034442df4e6 100644 --- a/inject/maven-plugin/pom.xml +++ b/inject/maven-plugin/pom.xml @@ -22,7 +22,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/pom.xml b/inject/pom.xml index 94c6ce56939..c812c0f1401 100644 --- a/inject/pom.xml +++ b/inject/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.inject diff --git a/inject/processor/pom.xml b/inject/processor/pom.xml index 9d551970764..88fdab1f564 100644 --- a/inject/processor/pom.xml +++ b/inject/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/runtime/pom.xml b/inject/runtime/pom.xml index a2d5362adac..2bf58fd3b04 100644 --- a/inject/runtime/pom.xml +++ b/inject/runtime/pom.xml @@ -22,7 +22,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/testing/pom.xml b/inject/testing/pom.xml index 516dba9e45e..0f02ff69f49 100644 --- a/inject/testing/pom.xml +++ b/inject/testing/pom.xml @@ -22,7 +22,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/api/pom.xml b/inject/tests/api/pom.xml index e230f04b248..ab7fe506024 100644 --- a/inject/tests/api/pom.xml +++ b/inject/tests/api/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.tests helidon-inject-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/interception/pom.xml b/inject/tests/interception/pom.xml index 8e0fd531d23..25b335d8100 100644 --- a/inject/tests/interception/pom.xml +++ b/inject/tests/interception/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.tests helidon-inject-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/pom.xml b/inject/tests/pom.xml index b747cd8dded..6448c4e0c92 100644 --- a/inject/tests/pom.xml +++ b/inject/tests/pom.xml @@ -22,7 +22,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/resources-inject/pom.xml b/inject/tests/resources-inject/pom.xml index fcc59a36920..12f6b35a5af 100644 --- a/inject/tests/resources-inject/pom.xml +++ b/inject/tests/resources-inject/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.tests helidon-inject-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/resources-plain/pom.xml b/inject/tests/resources-plain/pom.xml index b4520bbba8c..77911f45eab 100644 --- a/inject/tests/resources-plain/pom.xml +++ b/inject/tests/resources-plain/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.tests helidon-inject-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/runtime/pom.xml b/inject/tests/runtime/pom.xml index e151b76d2eb..cad380ab586 100644 --- a/inject/tests/runtime/pom.xml +++ b/inject/tests/runtime/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.tests helidon-inject-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tests/tck-jsr330/pom.xml b/inject/tests/tck-jsr330/pom.xml index a2e3acfd988..fb233f1d566 100644 --- a/inject/tests/tck-jsr330/pom.xml +++ b/inject/tests/tck-jsr330/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject.tests helidon-inject-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/inject/tools/pom.xml b/inject/tools/pom.xml index de249e51050..601ac83e6a4 100644 --- a/inject/tools/pom.xml +++ b/inject/tools/pom.xml @@ -23,7 +23,7 @@ io.helidon.inject helidon-inject-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/cdi/common-cdi/configurable/pom.xml b/integrations/cdi/common-cdi/configurable/pom.xml index dc47ea0dc2c..73b676435c5 100644 --- a/integrations/cdi/common-cdi/configurable/pom.xml +++ b/integrations/cdi/common-cdi/configurable/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-configurable Helidon CDI Integrations Common Configurable diff --git a/integrations/cdi/common-cdi/delegates/pom.xml b/integrations/cdi/common-cdi/delegates/pom.xml index 0c0b0f6db12..d430042e20e 100644 --- a/integrations/cdi/common-cdi/delegates/pom.xml +++ b/integrations/cdi/common-cdi/delegates/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-delegates Helidon CDI Integrations Common Delegates diff --git a/integrations/cdi/common-cdi/pom.xml b/integrations/cdi/common-cdi/pom.xml index 0ecd914eb87..a5e6f64a34b 100644 --- a/integrations/cdi/common-cdi/pom.xml +++ b/integrations/cdi/common-cdi/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-common-project pom diff --git a/integrations/cdi/common-cdi/reference-counted-context/pom.xml b/integrations/cdi/common-cdi/reference-counted-context/pom.xml index 533b1c72f94..efc6a9afa43 100644 --- a/integrations/cdi/common-cdi/reference-counted-context/pom.xml +++ b/integrations/cdi/common-cdi/reference-counted-context/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-reference-counted-context Helidon CDI Integrations Common Reference Counted Context diff --git a/integrations/cdi/datasource-hikaricp/pom.xml b/integrations/cdi/datasource-hikaricp/pom.xml index 63a213b3a2c..89e785d3f36 100644 --- a/integrations/cdi/datasource-hikaricp/pom.xml +++ b/integrations/cdi/datasource-hikaricp/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-datasource-hikaricp Helidon CDI Integrations HikariCP DataSource diff --git a/integrations/cdi/datasource-ucp/pom.xml b/integrations/cdi/datasource-ucp/pom.xml index 3dcb1ac1b68..1b7c097ee77 100644 --- a/integrations/cdi/datasource-ucp/pom.xml +++ b/integrations/cdi/datasource-ucp/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-datasource-ucp Helidon CDI Integrations UCP DataSource diff --git a/integrations/cdi/datasource/pom.xml b/integrations/cdi/datasource/pom.xml index ac11ec88115..aeba9874e9d 100644 --- a/integrations/cdi/datasource/pom.xml +++ b/integrations/cdi/datasource/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-datasource Helidon CDI Integrations DataSource diff --git a/integrations/cdi/eclipselink-cdi/pom.xml b/integrations/cdi/eclipselink-cdi/pom.xml index 2f4c236a07b..67363b43d39 100644 --- a/integrations/cdi/eclipselink-cdi/pom.xml +++ b/integrations/cdi/eclipselink-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-eclipselink Helidon CDI Integrations Eclipselink diff --git a/integrations/cdi/hibernate-cdi/pom.xml b/integrations/cdi/hibernate-cdi/pom.xml index bad5ddbedf9..d6eb41a92b6 100644 --- a/integrations/cdi/hibernate-cdi/pom.xml +++ b/integrations/cdi/hibernate-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-hibernate Helidon CDI Integrations Hibernate diff --git a/integrations/cdi/jpa-cdi/pom.xml b/integrations/cdi/jpa-cdi/pom.xml index 932ee440df8..8b9af26e73f 100644 --- a/integrations/cdi/jpa-cdi/pom.xml +++ b/integrations/cdi/jpa-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-jpa Helidon CDI Integrations JPA diff --git a/integrations/cdi/jta-cdi/pom.xml b/integrations/cdi/jta-cdi/pom.xml index 39dc178207d..d86d47f9a56 100644 --- a/integrations/cdi/jta-cdi/pom.xml +++ b/integrations/cdi/jta-cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-jta Helidon CDI Integrations JTA diff --git a/integrations/cdi/jta-weld/pom.xml b/integrations/cdi/jta-weld/pom.xml index 8a554b9ab1b..1c328127205 100644 --- a/integrations/cdi/jta-weld/pom.xml +++ b/integrations/cdi/jta-weld/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.cdi helidon-integrations-cdi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-cdi-jta-weld Helidon CDI Integrations JTA Weld diff --git a/integrations/cdi/pom.xml b/integrations/cdi/pom.xml index 81c4d0a665e..7f9902a57c5 100644 --- a/integrations/cdi/pom.xml +++ b/integrations/cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.cdi helidon-integrations-cdi-project diff --git a/integrations/common/pom.xml b/integrations/common/pom.xml index 4c69d5ac9b4..0d3fef1e4c8 100644 --- a/integrations/common/pom.xml +++ b/integrations/common/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/integrations/common/rest/pom.xml b/integrations/common/rest/pom.xml index 04a620bd52c..56d15f75e4a 100644 --- a/integrations/common/rest/pom.xml +++ b/integrations/common/rest/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.common helidon-integrations-common-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-common-rest diff --git a/integrations/db/h2/pom.xml b/integrations/db/h2/pom.xml index ff82bcfdf90..d4aa275c391 100644 --- a/integrations/db/h2/pom.xml +++ b/integrations/db/h2/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.db helidon-integrations-db-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT h2 diff --git a/integrations/db/mysql/pom.xml b/integrations/db/mysql/pom.xml index 8a0a064237b..c81e372d30e 100644 --- a/integrations/db/mysql/pom.xml +++ b/integrations/db/mysql/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.db helidon-integrations-db-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-db-mysql diff --git a/integrations/db/ojdbc/pom.xml b/integrations/db/ojdbc/pom.xml index 59410db95a3..0140eb5cf33 100644 --- a/integrations/db/ojdbc/pom.xml +++ b/integrations/db/ojdbc/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.db helidon-integrations-db-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ojdbc diff --git a/integrations/db/pgsql/pom.xml b/integrations/db/pgsql/pom.xml index dd33533065f..67b3369c624 100644 --- a/integrations/db/pgsql/pom.xml +++ b/integrations/db/pgsql/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.db helidon-integrations-db-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-db-pgsql diff --git a/integrations/db/pom.xml b/integrations/db/pom.xml index a081ed1d0e7..32def2e0f07 100644 --- a/integrations/db/pom.xml +++ b/integrations/db/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/integrations/graal/mp-native-image-extension/pom.xml b/integrations/graal/mp-native-image-extension/pom.xml index 763d848c25b..29af6547340 100644 --- a/integrations/graal/mp-native-image-extension/pom.xml +++ b/integrations/graal/mp-native-image-extension/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations.graal helidon-integrations-graal-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/integrations/graal/native-image-extension/pom.xml b/integrations/graal/native-image-extension/pom.xml index b2a94d48320..976be58c6a2 100644 --- a/integrations/graal/native-image-extension/pom.xml +++ b/integrations/graal/native-image-extension/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations.graal helidon-integrations-graal-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/integrations/graal/pom.xml b/integrations/graal/pom.xml index 7d6dfbacf42..bdc0a0e65fb 100644 --- a/integrations/graal/pom.xml +++ b/integrations/graal/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT diff --git a/integrations/jdbc/jdbc/pom.xml b/integrations/jdbc/jdbc/pom.xml index 970026a2486..3694f69b14e 100644 --- a/integrations/jdbc/jdbc/pom.xml +++ b/integrations/jdbc/jdbc/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-jdbc-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.jdbc diff --git a/integrations/jdbc/pom.xml b/integrations/jdbc/pom.xml index b4bd8889da3..ac7e11d9691 100644 --- a/integrations/jdbc/pom.xml +++ b/integrations/jdbc/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/integrations/jta/jdbc/pom.xml b/integrations/jta/jdbc/pom.xml index 63084981f34..6635e80f88f 100644 --- a/integrations/jta/jdbc/pom.xml +++ b/integrations/jta/jdbc/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-jta-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.jta diff --git a/integrations/jta/pom.xml b/integrations/jta/pom.xml index d1a65fe72df..a0963fac22a 100644 --- a/integrations/jta/pom.xml +++ b/integrations/jta/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/integrations/micrometer/cdi/pom.xml b/integrations/micrometer/cdi/pom.xml index dbf5d92f708..b1429cfabe3 100644 --- a/integrations/micrometer/cdi/pom.xml +++ b/integrations/micrometer/cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations helidon-integrations-micrometer-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.micrometer diff --git a/integrations/micrometer/micrometer/pom.xml b/integrations/micrometer/micrometer/pom.xml index 9fa2fc33776..e3bcdd04110 100644 --- a/integrations/micrometer/micrometer/pom.xml +++ b/integrations/micrometer/micrometer/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations helidon-integrations-micrometer-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.micrometer diff --git a/integrations/micrometer/pom.xml b/integrations/micrometer/pom.xml index a584342b312..5dada09cb5a 100644 --- a/integrations/micrometer/pom.xml +++ b/integrations/micrometer/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-micrometer-project Helidon Integrations Micrometer Project diff --git a/integrations/micronaut/cdi-processor/pom.xml b/integrations/micronaut/cdi-processor/pom.xml index 58fb7ada748..f857bf007f1 100644 --- a/integrations/micronaut/cdi-processor/pom.xml +++ b/integrations/micronaut/cdi-processor/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.micronaut helidon-integrations-micronaut-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-micronaut-cdi-processor diff --git a/integrations/micronaut/cdi/pom.xml b/integrations/micronaut/cdi/pom.xml index 8ad850510a1..db36aec526b 100644 --- a/integrations/micronaut/cdi/pom.xml +++ b/integrations/micronaut/cdi/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.micronaut helidon-integrations-micronaut-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-micronaut-cdi diff --git a/integrations/micronaut/data/pom.xml b/integrations/micronaut/data/pom.xml index 6a5c7e1e744..a101f282531 100644 --- a/integrations/micronaut/data/pom.xml +++ b/integrations/micronaut/data/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.micronaut helidon-integrations-micronaut-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-micronaut-data diff --git a/integrations/micronaut/pom.xml b/integrations/micronaut/pom.xml index 8c065a43ffc..c4aa6057eb1 100644 --- a/integrations/micronaut/pom.xml +++ b/integrations/micronaut/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.micronaut diff --git a/integrations/microstream/cache/pom.xml b/integrations/microstream/cache/pom.xml index 3923c889c7b..b5a4c58e6c4 100644 --- a/integrations/microstream/cache/pom.xml +++ b/integrations/microstream/cache/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.microstream helidon-integrations-microstream-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-microstream-cache diff --git a/integrations/microstream/cdi/pom.xml b/integrations/microstream/cdi/pom.xml index e383de26173..42130fc2081 100644 --- a/integrations/microstream/cdi/pom.xml +++ b/integrations/microstream/cdi/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.microstream helidon-integrations-microstream-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-microstream-cdi diff --git a/integrations/microstream/core/pom.xml b/integrations/microstream/core/pom.xml index 910a5866e54..d4cfad884ae 100644 --- a/integrations/microstream/core/pom.xml +++ b/integrations/microstream/core/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.microstream helidon-integrations-microstream-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-microstream diff --git a/integrations/microstream/health/pom.xml b/integrations/microstream/health/pom.xml index 42545514cf2..665f6ff6bce 100644 --- a/integrations/microstream/health/pom.xml +++ b/integrations/microstream/health/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.microstream helidon-integrations-microstream-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-microstream-health diff --git a/integrations/microstream/metrics/pom.xml b/integrations/microstream/metrics/pom.xml index eb11b2a164b..3bc5944b8c3 100644 --- a/integrations/microstream/metrics/pom.xml +++ b/integrations/microstream/metrics/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.microstream helidon-integrations-microstream-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-microstream-metrics diff --git a/integrations/microstream/pom.xml b/integrations/microstream/pom.xml index 30a0517c351..4009d16bb7d 100644 --- a/integrations/microstream/pom.xml +++ b/integrations/microstream/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.microstream diff --git a/integrations/neo4j/health/pom.xml b/integrations/neo4j/health/pom.xml index 4d706fc8bd0..c40cc968cc7 100644 --- a/integrations/neo4j/health/pom.xml +++ b/integrations/neo4j/health/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.neo4j helidon-integrations-neo4j-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-neo4j-health diff --git a/integrations/neo4j/metrics/pom.xml b/integrations/neo4j/metrics/pom.xml index 7ea2eb5a10d..01b6380e4df 100644 --- a/integrations/neo4j/metrics/pom.xml +++ b/integrations/neo4j/metrics/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.neo4j helidon-integrations-neo4j-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-neo4j-metrics diff --git a/integrations/neo4j/neo4j/pom.xml b/integrations/neo4j/neo4j/pom.xml index 1549b55b073..e76c111eccc 100644 --- a/integrations/neo4j/neo4j/pom.xml +++ b/integrations/neo4j/neo4j/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.neo4j helidon-integrations-neo4j-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-neo4j diff --git a/integrations/neo4j/pom.xml b/integrations/neo4j/pom.xml index e47b5c7a8b3..7730325d7f8 100644 --- a/integrations/neo4j/pom.xml +++ b/integrations/neo4j/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.neo4j helidon-integrations-neo4j-project diff --git a/integrations/oci/metrics/cdi/pom.xml b/integrations/oci/metrics/cdi/pom.xml index 5dbd305cbcd..2b0b12bfdcd 100644 --- a/integrations/oci/metrics/cdi/pom.xml +++ b/integrations/oci/metrics/cdi/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.oci.metrics helidon-integrations-oci-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-oci-metrics-cdi Helidon Integrations OCI Metrics CDI diff --git a/integrations/oci/metrics/metrics/pom.xml b/integrations/oci/metrics/metrics/pom.xml index e7f89f2e04f..33f634d462a 100644 --- a/integrations/oci/metrics/metrics/pom.xml +++ b/integrations/oci/metrics/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.oci.metrics helidon-integrations-oci-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-oci-metrics Helidon Integrations OCI Metrics diff --git a/integrations/oci/metrics/pom.xml b/integrations/oci/metrics/pom.xml index 6fb06626db2..56c96a012e3 100644 --- a/integrations/oci/metrics/pom.xml +++ b/integrations/oci/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.oci helidon-integrations-oci-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.oci.metrics helidon-integrations-oci-metrics-project diff --git a/integrations/oci/oci/pom.xml b/integrations/oci/oci/pom.xml index 2ab93efb089..f89ec276414 100644 --- a/integrations/oci/oci/pom.xml +++ b/integrations/oci/oci/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations.oci helidon-integrations-oci-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-oci Helidon Integrations OCI diff --git a/integrations/oci/pom.xml b/integrations/oci/pom.xml index cf8b46b4133..34af212bf48 100644 --- a/integrations/oci/pom.xml +++ b/integrations/oci/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.oci diff --git a/integrations/oci/sdk/cdi/pom.xml b/integrations/oci/sdk/cdi/pom.xml index 317c0ce3393..d1b216d8a13 100644 --- a/integrations/oci/sdk/cdi/pom.xml +++ b/integrations/oci/sdk/cdi/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.oci.sdk helidon-integrations-oci-sdk-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-oci-sdk-cdi Helidon Integrations OCI SDK CDI diff --git a/integrations/oci/sdk/pom.xml b/integrations/oci/sdk/pom.xml index a574fdb46db..7b65f2cc3b2 100644 --- a/integrations/oci/sdk/pom.xml +++ b/integrations/oci/sdk/pom.xml @@ -24,7 +24,7 @@ io.helidon.integrations.oci helidon-integrations-oci-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.integrations.oci.sdk diff --git a/integrations/oci/sdk/processor/pom.xml b/integrations/oci/sdk/processor/pom.xml index 46418d84047..c28acd8ad97 100644 --- a/integrations/oci/sdk/processor/pom.xml +++ b/integrations/oci/sdk/processor/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci.sdk helidon-integrations-oci-sdk-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/oci/sdk/runtime/pom.xml b/integrations/oci/sdk/runtime/pom.xml index 158f2e10cfd..9c79a088106 100644 --- a/integrations/oci/sdk/runtime/pom.xml +++ b/integrations/oci/sdk/runtime/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci.sdk helidon-integrations-oci-sdk-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/oci/sdk/tests/pom.xml b/integrations/oci/sdk/tests/pom.xml index f40e5298e46..be0637aa3f5 100644 --- a/integrations/oci/sdk/tests/pom.xml +++ b/integrations/oci/sdk/tests/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci.sdk helidon-integrations-oci-sdk-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/oci/sdk/tests/test-application/pom.xml b/integrations/oci/sdk/tests/test-application/pom.xml index 067ad22dc27..3de8d45d785 100644 --- a/integrations/oci/sdk/tests/test-application/pom.xml +++ b/integrations/oci/sdk/tests/test-application/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci.sdk.tests helidon-integrations-oci-sdk-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/oci/sdk/tests/test-module1/pom.xml b/integrations/oci/sdk/tests/test-module1/pom.xml index 14d01da15c9..7506874c072 100644 --- a/integrations/oci/sdk/tests/test-module1/pom.xml +++ b/integrations/oci/sdk/tests/test-module1/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci.sdk.tests helidon-integrations-oci-sdk-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/oci/sdk/tests/test-module2/pom.xml b/integrations/oci/sdk/tests/test-module2/pom.xml index 4366cdfe007..7b617714788 100644 --- a/integrations/oci/sdk/tests/test-module2/pom.xml +++ b/integrations/oci/sdk/tests/test-module2/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci.sdk.tests helidon-integrations-oci-sdk-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/integrations/oci/secrets-config-source/pom.xml b/integrations/oci/secrets-config-source/pom.xml index 4c864284738..56c7bd287b3 100644 --- a/integrations/oci/secrets-config-source/pom.xml +++ b/integrations/oci/secrets-config-source/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations.oci helidon-integrations-oci-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-oci-secrets-config-source Helidon Integrations OCI Secrets Config Source diff --git a/integrations/oci/secrets-mp-config-source/pom.xml b/integrations/oci/secrets-mp-config-source/pom.xml index 1ae3ad4c2b0..937a8b40e9e 100644 --- a/integrations/oci/secrets-mp-config-source/pom.xml +++ b/integrations/oci/secrets-mp-config-source/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations.oci helidon-integrations-oci-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-oci-secrets-mp-config-source Helidon Integrations OCI Secrets MP Config Source diff --git a/integrations/oci/tls-certificates/pom.xml b/integrations/oci/tls-certificates/pom.xml index 6846f6d98a7..2b53e02903c 100644 --- a/integrations/oci/tls-certificates/pom.xml +++ b/integrations/oci/tls-certificates/pom.xml @@ -23,7 +23,7 @@ io.helidon.integrations.oci helidon-integrations-oci-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/integrations/openapi-ui/pom.xml b/integrations/openapi-ui/pom.xml index 2e55970d118..e90e5367ee3 100644 --- a/integrations/openapi-ui/pom.xml +++ b/integrations/openapi-ui/pom.xml @@ -21,7 +21,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.openapi-ui diff --git a/integrations/pom.xml b/integrations/pom.xml index b4334b274df..5175ab424d9 100644 --- a/integrations/pom.xml +++ b/integrations/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations helidon-integrations-project diff --git a/integrations/vault/auths/approle/pom.xml b/integrations/vault/auths/approle/pom.xml index 640cad9e057..cd31ed2272c 100644 --- a/integrations/vault/auths/approle/pom.xml +++ b/integrations/vault/auths/approle/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.auths helidon-integrations-vault-auths-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-auths-approle diff --git a/integrations/vault/auths/common/pom.xml b/integrations/vault/auths/common/pom.xml index ab6d2923870..6d40fcd3286 100644 --- a/integrations/vault/auths/common/pom.xml +++ b/integrations/vault/auths/common/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.auths helidon-integrations-vault-auths-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-auths-common diff --git a/integrations/vault/auths/k8s/pom.xml b/integrations/vault/auths/k8s/pom.xml index e9635909d13..f91876580e4 100644 --- a/integrations/vault/auths/k8s/pom.xml +++ b/integrations/vault/auths/k8s/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.auths helidon-integrations-vault-auths-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-auths-k8s diff --git a/integrations/vault/auths/pom.xml b/integrations/vault/auths/pom.xml index f1ced1aa721..8816fadc6ea 100644 --- a/integrations/vault/auths/pom.xml +++ b/integrations/vault/auths/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault helidon-integrations-vault-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/integrations/vault/auths/token/pom.xml b/integrations/vault/auths/token/pom.xml index 81391be5a56..cfd22aafaa1 100644 --- a/integrations/vault/auths/token/pom.xml +++ b/integrations/vault/auths/token/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.auths helidon-integrations-vault-auths-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-auths-token diff --git a/integrations/vault/cdi/pom.xml b/integrations/vault/cdi/pom.xml index 544ed8a32af..c39dfa2a9cf 100644 --- a/integrations/vault/cdi/pom.xml +++ b/integrations/vault/cdi/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault helidon-integrations-vault-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-cdi diff --git a/integrations/vault/pom.xml b/integrations/vault/pom.xml index bf33aeeae5e..ea3cdcd84d7 100644 --- a/integrations/vault/pom.xml +++ b/integrations/vault/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations helidon-integrations-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.integrations.vault diff --git a/integrations/vault/secrets/cubbyhole/pom.xml b/integrations/vault/secrets/cubbyhole/pom.xml index a0146c11720..9c22f7f8e98 100644 --- a/integrations/vault/secrets/cubbyhole/pom.xml +++ b/integrations/vault/secrets/cubbyhole/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.secrets helidon-integrations-vault-secrets-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-secrets-cubbyhole diff --git a/integrations/vault/secrets/database/pom.xml b/integrations/vault/secrets/database/pom.xml index e2f01bd8674..846f638e126 100644 --- a/integrations/vault/secrets/database/pom.xml +++ b/integrations/vault/secrets/database/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.secrets helidon-integrations-vault-secrets-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-secrets-database diff --git a/integrations/vault/secrets/kv1/pom.xml b/integrations/vault/secrets/kv1/pom.xml index fe91253d2ce..099f361c9ee 100644 --- a/integrations/vault/secrets/kv1/pom.xml +++ b/integrations/vault/secrets/kv1/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.secrets helidon-integrations-vault-secrets-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-secrets-kv1 diff --git a/integrations/vault/secrets/kv2/pom.xml b/integrations/vault/secrets/kv2/pom.xml index 3a032c3222d..0d826ebf2d1 100644 --- a/integrations/vault/secrets/kv2/pom.xml +++ b/integrations/vault/secrets/kv2/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.secrets helidon-integrations-vault-secrets-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-secrets-kv2 diff --git a/integrations/vault/secrets/pki/pom.xml b/integrations/vault/secrets/pki/pom.xml index a5991fdb40e..7070ca59f75 100644 --- a/integrations/vault/secrets/pki/pom.xml +++ b/integrations/vault/secrets/pki/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.secrets helidon-integrations-vault-secrets-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-secrets-pki diff --git a/integrations/vault/secrets/pom.xml b/integrations/vault/secrets/pom.xml index a812403b8a0..f99dd58d038 100644 --- a/integrations/vault/secrets/pom.xml +++ b/integrations/vault/secrets/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault helidon-integrations-vault-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/integrations/vault/secrets/transit/pom.xml b/integrations/vault/secrets/transit/pom.xml index b4eb84308f6..b95f528726c 100644 --- a/integrations/vault/secrets/transit/pom.xml +++ b/integrations/vault/secrets/transit/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault.secrets helidon-integrations-vault-secrets-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault-secrets-transit diff --git a/integrations/vault/sys/pom.xml b/integrations/vault/sys/pom.xml index 68adb2e9dc1..404f35651d2 100644 --- a/integrations/vault/sys/pom.xml +++ b/integrations/vault/sys/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault helidon-integrations-vault-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/integrations/vault/sys/sys/pom.xml b/integrations/vault/sys/sys/pom.xml index 3ab7f532d1e..26382c0bce0 100644 --- a/integrations/vault/sys/sys/pom.xml +++ b/integrations/vault/sys/sys/pom.xml @@ -21,7 +21,7 @@ helidon-integrations-vault-sys-project io.helidon.integrations.vault.sys - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/integrations/vault/vault/pom.xml b/integrations/vault/vault/pom.xml index 340c4e996ee..0949545b738 100644 --- a/integrations/vault/vault/pom.xml +++ b/integrations/vault/vault/pom.xml @@ -22,7 +22,7 @@ io.helidon.integrations.vault helidon-integrations-vault-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-integrations-vault diff --git a/jersey/client/pom.xml b/jersey/client/pom.xml index fd52adafb6c..7707bdc9d35 100644 --- a/jersey/client/pom.xml +++ b/jersey/client/pom.xml @@ -21,7 +21,7 @@ io.helidon.jersey helidon-jersey-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/jersey/common/pom.xml b/jersey/common/pom.xml index a7f7c29f149..aa2b2faac33 100644 --- a/jersey/common/pom.xml +++ b/jersey/common/pom.xml @@ -21,7 +21,7 @@ helidon-jersey-project io.helidon.jersey - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/jersey/connector/pom.xml b/jersey/connector/pom.xml index 5996f49409d..8406160726c 100644 --- a/jersey/connector/pom.xml +++ b/jersey/connector/pom.xml @@ -21,7 +21,7 @@ helidon-jersey-project io.helidon.jersey - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/jersey/jsonp/pom.xml b/jersey/jsonp/pom.xml index 19985b97a0f..abf9ee63144 100644 --- a/jersey/jsonp/pom.xml +++ b/jersey/jsonp/pom.xml @@ -21,7 +21,7 @@ helidon-jersey-project io.helidon.jersey - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/jersey/pom.xml b/jersey/pom.xml index bf85a3704d0..bebc32ddc40 100644 --- a/jersey/pom.xml +++ b/jersey/pom.xml @@ -21,7 +21,7 @@ helidon-project io.helidon - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/jersey/server/pom.xml b/jersey/server/pom.xml index 4950e95a3d1..2cb489bb706 100644 --- a/jersey/server/pom.xml +++ b/jersey/server/pom.xml @@ -22,7 +22,7 @@ io.helidon.jersey helidon-jersey-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-jersey-server diff --git a/jersey/tests/connector/pom.xml b/jersey/tests/connector/pom.xml index 7905e887d2d..efaa6303f11 100644 --- a/jersey/tests/connector/pom.xml +++ b/jersey/tests/connector/pom.xml @@ -21,7 +21,7 @@ io.helidon.jersey.tests helidon-jersey-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-jersey-tests-connector diff --git a/jersey/tests/pom.xml b/jersey/tests/pom.xml index 6dac9736293..cd60264fdcb 100644 --- a/jersey/tests/pom.xml +++ b/jersey/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.jersey helidon-jersey-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.jersey.tests diff --git a/licensing/pom.xml b/licensing/pom.xml index 2e2f5bfa4f0..b9bea0cc9d5 100644 --- a/licensing/pom.xml +++ b/licensing/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.licensing helidon-licensing diff --git a/logging/common/pom.xml b/logging/common/pom.xml index 91cb68763c8..6bdd030476f 100644 --- a/logging/common/pom.xml +++ b/logging/common/pom.xml @@ -21,7 +21,7 @@ helidon-logging-project io.helidon.logging - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-logging-common diff --git a/logging/jul/pom.xml b/logging/jul/pom.xml index 11bd0a591c1..bf589f75022 100644 --- a/logging/jul/pom.xml +++ b/logging/jul/pom.xml @@ -20,7 +20,7 @@ helidon-logging-project io.helidon.logging - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/logging/log4j/pom.xml b/logging/log4j/pom.xml index 08c54a93830..dd2f7ea6b7d 100644 --- a/logging/log4j/pom.xml +++ b/logging/log4j/pom.xml @@ -20,7 +20,7 @@ helidon-logging-project io.helidon.logging - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/logging/pom.xml b/logging/pom.xml index e2858a1e419..0a3cacfcb38 100644 --- a/logging/pom.xml +++ b/logging/pom.xml @@ -21,7 +21,7 @@ helidon-project io.helidon - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.logging diff --git a/logging/slf4j/pom.xml b/logging/slf4j/pom.xml index ebfd580c47c..4f99fc4ea33 100644 --- a/logging/slf4j/pom.xml +++ b/logging/slf4j/pom.xml @@ -21,7 +21,7 @@ helidon-logging-project io.helidon.logging - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-logging-slf4j diff --git a/logging/tests/log4j/pom.xml b/logging/tests/log4j/pom.xml index c59d6bcb9da..1b666b7430a 100644 --- a/logging/tests/log4j/pom.xml +++ b/logging/tests/log4j/pom.xml @@ -21,7 +21,7 @@ io.helidon.logging.tests helidon-logging-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-logging-tests-log4j diff --git a/logging/tests/pom.xml b/logging/tests/pom.xml index 935a4ee3fd9..197b433b9fb 100644 --- a/logging/tests/pom.xml +++ b/logging/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.logging helidon-logging-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.logging.tests diff --git a/lra/coordinator/client/narayana-client/pom.xml b/lra/coordinator/client/narayana-client/pom.xml index 5a271b1b677..a1e8964b0c0 100644 --- a/lra/coordinator/client/narayana-client/pom.xml +++ b/lra/coordinator/client/narayana-client/pom.xml @@ -24,7 +24,7 @@ io.helidon.lra helidon-lra-coordinator-client-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-lra-coordinator-narayana-client diff --git a/lra/coordinator/client/pom.xml b/lra/coordinator/client/pom.xml index 2fe7560d6c3..0ff6735e772 100644 --- a/lra/coordinator/client/pom.xml +++ b/lra/coordinator/client/pom.xml @@ -24,7 +24,7 @@ io.helidon.lra helidon-lra-coordinator-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.lra helidon-lra-coordinator-client-project diff --git a/lra/coordinator/client/spi/pom.xml b/lra/coordinator/client/spi/pom.xml index 243ab3e2852..8470fc2ba28 100644 --- a/lra/coordinator/client/spi/pom.xml +++ b/lra/coordinator/client/spi/pom.xml @@ -24,7 +24,7 @@ io.helidon.lra helidon-lra-coordinator-client-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-lra-coordinator-client-spi diff --git a/lra/coordinator/pom.xml b/lra/coordinator/pom.xml index d0354358f4d..a4bea92f804 100644 --- a/lra/coordinator/pom.xml +++ b/lra/coordinator/pom.xml @@ -24,7 +24,7 @@ io.helidon.lra helidon-lra-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.lra helidon-lra-coordinator-project diff --git a/lra/coordinator/server/pom.xml b/lra/coordinator/server/pom.xml index bffbde64d02..cfedfd763fa 100644 --- a/lra/coordinator/server/pom.xml +++ b/lra/coordinator/server/pom.xml @@ -25,7 +25,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml diff --git a/lra/pom.xml b/lra/pom.xml index e9e33dc9d7c..3f4e71c6b33 100644 --- a/lra/pom.xml +++ b/lra/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.lra helidon-lra-project diff --git a/messaging/connectors/aq/pom.xml b/messaging/connectors/aq/pom.xml index 7702547c534..9b294994814 100644 --- a/messaging/connectors/aq/pom.xml +++ b/messaging/connectors/aq/pom.xml @@ -24,7 +24,7 @@ io.helidon.messaging helidon-messaging-connectors-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.messaging.aq diff --git a/messaging/connectors/jms-shim/pom.xml b/messaging/connectors/jms-shim/pom.xml index 02a4bd729d5..3e368497f6c 100644 --- a/messaging/connectors/jms-shim/pom.xml +++ b/messaging/connectors/jms-shim/pom.xml @@ -24,7 +24,7 @@ io.helidon.messaging helidon-messaging-connectors-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-messaging-jms-shim diff --git a/messaging/connectors/jms/pom.xml b/messaging/connectors/jms/pom.xml index 95974eda074..bbe8b062535 100644 --- a/messaging/connectors/jms/pom.xml +++ b/messaging/connectors/jms/pom.xml @@ -24,7 +24,7 @@ io.helidon.messaging helidon-messaging-connectors-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.messaging.jms diff --git a/messaging/connectors/kafka/pom.xml b/messaging/connectors/kafka/pom.xml index 3c45dfad0cd..4dd8f1870cf 100644 --- a/messaging/connectors/kafka/pom.xml +++ b/messaging/connectors/kafka/pom.xml @@ -25,7 +25,7 @@ io.helidon.messaging helidon-messaging-connectors-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.messaging.kafka diff --git a/messaging/connectors/mock/pom.xml b/messaging/connectors/mock/pom.xml index 040dca685a9..aaf467f8859 100644 --- a/messaging/connectors/mock/pom.xml +++ b/messaging/connectors/mock/pom.xml @@ -24,7 +24,7 @@ io.helidon.messaging helidon-messaging-connectors-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.messaging.mock diff --git a/messaging/connectors/pom.xml b/messaging/connectors/pom.xml index 86604e72f98..b228af13ea7 100644 --- a/messaging/connectors/pom.xml +++ b/messaging/connectors/pom.xml @@ -23,7 +23,7 @@ io.helidon.messaging helidon-messaging-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-messaging-connectors-project Helidon Messaging Connectors Project diff --git a/messaging/connectors/wls-jms/pom.xml b/messaging/connectors/wls-jms/pom.xml index 896c1066d9e..fb06c76e3e9 100644 --- a/messaging/connectors/wls-jms/pom.xml +++ b/messaging/connectors/wls-jms/pom.xml @@ -24,7 +24,7 @@ io.helidon.messaging helidon-messaging-connectors-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.messaging.wls-jms diff --git a/messaging/messaging/pom.xml b/messaging/messaging/pom.xml index 466937f97e3..df8cc937c15 100644 --- a/messaging/messaging/pom.xml +++ b/messaging/messaging/pom.xml @@ -23,7 +23,7 @@ io.helidon.messaging helidon-messaging-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-messaging diff --git a/messaging/pom.xml b/messaging/pom.xml index 1ddcf95bac2..76b2371a4e9 100644 --- a/messaging/pom.xml +++ b/messaging/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.messaging helidon-messaging-project diff --git a/metrics/api/pom.xml b/metrics/api/pom.xml index 34cf02a3e91..eaef22a1010 100644 --- a/metrics/api/pom.xml +++ b/metrics/api/pom.xml @@ -22,7 +22,7 @@ io.helidon.metrics helidon-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-metrics-api diff --git a/metrics/metrics/pom.xml b/metrics/metrics/pom.xml index 582e0d29e1e..a293bd7ef92 100644 --- a/metrics/metrics/pom.xml +++ b/metrics/metrics/pom.xml @@ -24,7 +24,7 @@ io.helidon.metrics helidon-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-metrics Helidon Metrics diff --git a/metrics/pom.xml b/metrics/pom.xml index 2698863554a..3a2474738f6 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.metrics diff --git a/metrics/prometheus/pom.xml b/metrics/prometheus/pom.xml index 744751650b5..f19cd578d34 100644 --- a/metrics/prometheus/pom.xml +++ b/metrics/prometheus/pom.xml @@ -24,7 +24,7 @@ io.helidon.metrics helidon-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-metrics-prometheus Helidon Metrics Prometheus diff --git a/metrics/provider-tests/pom.xml b/metrics/provider-tests/pom.xml index 5c4a9e2a3db..08de79bb9e0 100644 --- a/metrics/provider-tests/pom.xml +++ b/metrics/provider-tests/pom.xml @@ -23,7 +23,7 @@ io.helidon.metrics helidon-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-metrics-provider-tests Helidon Metrics Provider Tests diff --git a/metrics/providers/micrometer/pom.xml b/metrics/providers/micrometer/pom.xml index 3bed1d983a3..89847d75cce 100644 --- a/metrics/providers/micrometer/pom.xml +++ b/metrics/providers/micrometer/pom.xml @@ -24,7 +24,7 @@ io.helidon.metrics.providers helidon-metrics-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-metrics-providers-micrometer Helidon Metrics Providers Micrometer diff --git a/metrics/providers/pom.xml b/metrics/providers/pom.xml index 2c3e01261c9..411626ee467 100644 --- a/metrics/providers/pom.xml +++ b/metrics/providers/pom.xml @@ -22,7 +22,7 @@ io.helidon.metrics helidon-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/metrics/system-meters/pom.xml b/metrics/system-meters/pom.xml index f0678bb2b13..b9f997a8b83 100644 --- a/metrics/system-meters/pom.xml +++ b/metrics/system-meters/pom.xml @@ -22,7 +22,7 @@ io.helidon.metrics helidon-metrics-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-metrics-system-meters diff --git a/metrics/trace-exemplar/pom.xml b/metrics/trace-exemplar/pom.xml index 0505f2841d7..79078238d1e 100644 --- a/metrics/trace-exemplar/pom.xml +++ b/metrics/trace-exemplar/pom.xml @@ -23,7 +23,7 @@ helidon-metrics-project io.helidon.metrics - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/access-log/pom.xml b/microprofile/access-log/pom.xml index 60d60a79379..f5a2cb6bf52 100644 --- a/microprofile/access-log/pom.xml +++ b/microprofile/access-log/pom.xml @@ -23,7 +23,7 @@ helidon-microprofile-project io.helidon.microprofile - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/bean-validation/pom.xml b/microprofile/bean-validation/pom.xml index 600ba6153ed..aa3cfed9bb9 100644 --- a/microprofile/bean-validation/pom.xml +++ b/microprofile/bean-validation/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.bean-validation helidon-microprofile-bean-validation diff --git a/microprofile/bundles/helidon-microprofile-core/pom.xml b/microprofile/bundles/helidon-microprofile-core/pom.xml index c805ffddb5e..643efbdcbb6 100644 --- a/microprofile/bundles/helidon-microprofile-core/pom.xml +++ b/microprofile/bundles/helidon-microprofile-core/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.bundles bundles-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-core Helidon Microprofile Core Bundle diff --git a/microprofile/bundles/helidon-microprofile/pom.xml b/microprofile/bundles/helidon-microprofile/pom.xml index a275b306b0c..0bd3e8d1e0d 100644 --- a/microprofile/bundles/helidon-microprofile/pom.xml +++ b/microprofile/bundles/helidon-microprofile/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.bundles bundles-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile Helidon Microprofile Full Bundle diff --git a/microprofile/bundles/pom.xml b/microprofile/bundles/pom.xml index 1ace9661fb2..8b2141ab83f 100644 --- a/microprofile/bundles/pom.xml +++ b/microprofile/bundles/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.microprofile.bundles diff --git a/microprofile/cdi/pom.xml b/microprofile/cdi/pom.xml index 41271961d02..c8b0dd515e9 100644 --- a/microprofile/cdi/pom.xml +++ b/microprofile/cdi/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.cdi diff --git a/microprofile/config/pom.xml b/microprofile/config/pom.xml index 09c24b81cca..799e2a2ec5d 100644 --- a/microprofile/config/pom.xml +++ b/microprofile/config/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.config helidon-microprofile-config diff --git a/microprofile/cors/pom.xml b/microprofile/cors/pom.xml index 1a9123a305f..4ff69f17402 100644 --- a/microprofile/cors/pom.xml +++ b/microprofile/cors/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-cors diff --git a/microprofile/fault-tolerance/pom.xml b/microprofile/fault-tolerance/pom.xml index 797eba92d9a..895c13bb7d3 100644 --- a/microprofile/fault-tolerance/pom.xml +++ b/microprofile/fault-tolerance/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-fault-tolerance Helidon Microprofile Fault Tolerance diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index 485fc0d5cc1..99cfb08e03f 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -20,7 +20,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 8d16b77194b..4ad742156e0 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -20,7 +20,7 @@ io.helidon.microprofile.graphql helidon-microprofile-graphql - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/health/pom.xml b/microprofile/health/pom.xml index c5304efabfc..d4e7b3362a4 100644 --- a/microprofile/health/pom.xml +++ b/microprofile/health/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.health helidon-microprofile-health diff --git a/microprofile/jwt-auth/pom.xml b/microprofile/jwt-auth/pom.xml index f98c33bd325..f8e3ad829bf 100644 --- a/microprofile/jwt-auth/pom.xml +++ b/microprofile/jwt-auth/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.jwt diff --git a/microprofile/lra/jax-rs/pom.xml b/microprofile/lra/jax-rs/pom.xml index 9ec95a235d0..21a0b9fb708 100644 --- a/microprofile/lra/jax-rs/pom.xml +++ b/microprofile/lra/jax-rs/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.lra helidon-microprofile-lra-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-lra diff --git a/microprofile/lra/pom.xml b/microprofile/lra/pom.xml index 85affcfdb35..1448ff3df54 100644 --- a/microprofile/lra/pom.xml +++ b/microprofile/lra/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.lra diff --git a/microprofile/messaging/core/pom.xml b/microprofile/messaging/core/pom.xml index 6fbca416134..eb8a1d95b98 100644 --- a/microprofile/messaging/core/pom.xml +++ b/microprofile/messaging/core/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.messaging helidon-microprofile-messaging-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-messaging diff --git a/microprofile/messaging/health/pom.xml b/microprofile/messaging/health/pom.xml index a93032f8289..db4a7fd548b 100644 --- a/microprofile/messaging/health/pom.xml +++ b/microprofile/messaging/health/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.messaging helidon-microprofile-messaging-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-messaging-health diff --git a/microprofile/messaging/metrics/pom.xml b/microprofile/messaging/metrics/pom.xml index 256634f45e1..2fc436f3ddf 100644 --- a/microprofile/messaging/metrics/pom.xml +++ b/microprofile/messaging/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.messaging helidon-microprofile-messaging-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-messaging-metrics diff --git a/microprofile/messaging/pom.xml b/microprofile/messaging/pom.xml index bcb7803cee2..9459a75c439 100644 --- a/microprofile/messaging/pom.xml +++ b/microprofile/messaging/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.messaging diff --git a/microprofile/metrics/pom.xml b/microprofile/metrics/pom.xml index ca90332f058..72025101774 100644 --- a/microprofile/metrics/pom.xml +++ b/microprofile/metrics/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.metrics helidon-microprofile-metrics diff --git a/microprofile/oidc/pom.xml b/microprofile/oidc/pom.xml index a0e75f76d53..8b498d5aa64 100644 --- a/microprofile/oidc/pom.xml +++ b/microprofile/oidc/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-oidc Helidon Microprofile Security OIDC Integration diff --git a/microprofile/openapi/pom.xml b/microprofile/openapi/pom.xml index 497fb9e610d..65a26ec1aa3 100644 --- a/microprofile/openapi/pom.xml +++ b/microprofile/openapi/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.openapi helidon-microprofile-openapi diff --git a/microprofile/pom.xml b/microprofile/pom.xml index c783c722044..327b8b55752 100644 --- a/microprofile/pom.xml +++ b/microprofile/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.microprofile diff --git a/microprofile/reactive-streams/pom.xml b/microprofile/reactive-streams/pom.xml index cd72baf732c..8532a8f57ce 100644 --- a/microprofile/reactive-streams/pom.xml +++ b/microprofile/reactive-streams/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.reactive-streams diff --git a/microprofile/rest-client/pom.xml b/microprofile/rest-client/pom.xml index 861de602ff7..af4c65e87eb 100644 --- a/microprofile/rest-client/pom.xml +++ b/microprofile/rest-client/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.rest-client diff --git a/microprofile/scheduling/pom.xml b/microprofile/scheduling/pom.xml index d3b382f148d..875690936bc 100644 --- a/microprofile/scheduling/pom.xml +++ b/microprofile/scheduling/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.scheduling diff --git a/microprofile/security/pom.xml b/microprofile/security/pom.xml index cf1cbf12a98..de84ae33c25 100644 --- a/microprofile/security/pom.xml +++ b/microprofile/security/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-microprofile-security Helidon Microprofile Security Integration diff --git a/microprofile/server/pom.xml b/microprofile/server/pom.xml index 5724895cb05..73b33048e5e 100644 --- a/microprofile/server/pom.xml +++ b/microprofile/server/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.server helidon-microprofile-server diff --git a/microprofile/service-common/pom.xml b/microprofile/service-common/pom.xml index 1933e6a6d92..f34b650cb5d 100644 --- a/microprofile/service-common/pom.xml +++ b/microprofile/service-common/pom.xml @@ -20,7 +20,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/telemetry/pom.xml b/microprofile/telemetry/pom.xml index c6dc6207e0e..300b21bf105 100644 --- a/microprofile/telemetry/pom.xml +++ b/microprofile/telemetry/pom.xml @@ -22,7 +22,7 @@ helidon-microprofile-project io.helidon.microprofile - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.telemetry helidon-microprofile-telemetry diff --git a/microprofile/testing/junit5/pom.xml b/microprofile/testing/junit5/pom.xml index ba84605ac03..e3e8fa644b9 100644 --- a/microprofile/testing/junit5/pom.xml +++ b/microprofile/testing/junit5/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.testing helidon-microprofile-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/microprofile/testing/mocking/pom.xml b/microprofile/testing/mocking/pom.xml index f87d42df88e..0a401c43914 100644 --- a/microprofile/testing/mocking/pom.xml +++ b/microprofile/testing/mocking/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.testing helidon-microprofile-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/microprofile/testing/pom.xml b/microprofile/testing/pom.xml index 3bd61ad034c..c05624f8cb9 100644 --- a/microprofile/testing/pom.xml +++ b/microprofile/testing/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/testing/testng/pom.xml b/microprofile/testing/testng/pom.xml index 6f2e6a0b19a..dcb1719ea7b 100644 --- a/microprofile/testing/testng/pom.xml +++ b/microprofile/testing/testng/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.testing helidon-microprofile-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/microprofile/tests/arquillian/pom.xml b/microprofile/tests/arquillian/pom.xml index 371fac398d7..5b42fb8fa64 100644 --- a/microprofile/tests/arquillian/pom.xml +++ b/microprofile/tests/arquillian/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests helidon-microprofile-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-arquillian diff --git a/microprofile/tests/config/pom.xml b/microprofile/tests/config/pom.xml index 6c9e27cbb90..c92a5b44925 100644 --- a/microprofile/tests/config/pom.xml +++ b/microprofile/tests/config/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests helidon-microprofile-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-config diff --git a/microprofile/tests/pom.xml b/microprofile/tests/pom.xml index 22e71eaccf9..d716e0e7bfa 100644 --- a/microprofile/tests/pom.xml +++ b/microprofile/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/tests/server/pom.xml b/microprofile/tests/server/pom.xml index b6c3f80cae1..b605af06a42 100644 --- a/microprofile/tests/server/pom.xml +++ b/microprofile/tests/server/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.tests helidon-microprofile-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/tck/pom.xml b/microprofile/tests/tck/pom.xml index d6f1acc205b..4932cc40478 100644 --- a/microprofile/tests/tck/pom.xml +++ b/microprofile/tests/tck/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.tests helidon-microprofile-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/tck/tck-annotations/pom.xml b/microprofile/tests/tck/tck-annotations/pom.xml index 0a0f6241498..9ac148e032b 100644 --- a/microprofile/tests/tck/tck-annotations/pom.xml +++ b/microprofile/tests/tck/tck-annotations/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-annotations diff --git a/microprofile/tests/tck/tck-cdi-lang-model/pom.xml b/microprofile/tests/tck/tck-cdi-lang-model/pom.xml index 672ed2bceea..91af39808a6 100644 --- a/microprofile/tests/tck/tck-cdi-lang-model/pom.xml +++ b/microprofile/tests/tck/tck-cdi-lang-model/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-cdi-lang-model diff --git a/microprofile/tests/tck/tck-cdi/pom.xml b/microprofile/tests/tck/tck-cdi/pom.xml index c52c86479dc..6e7357eb93d 100644 --- a/microprofile/tests/tck/tck-cdi/pom.xml +++ b/microprofile/tests/tck/tck-cdi/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-cdi diff --git a/microprofile/tests/tck/tck-config/pom.xml b/microprofile/tests/tck/tck-config/pom.xml index f167b0a361a..d230939218e 100644 --- a/microprofile/tests/tck/tck-config/pom.xml +++ b/microprofile/tests/tck/tck-config/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-config diff --git a/microprofile/tests/tck/tck-core-profile/pom.xml b/microprofile/tests/tck/tck-core-profile/pom.xml index 2ae0139d62d..11bf29a6a0f 100644 --- a/microprofile/tests/tck/tck-core-profile/pom.xml +++ b/microprofile/tests/tck/tck-core-profile/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/tests/tck/tck-core-profile/tck-core-profile-test/pom.xml b/microprofile/tests/tck/tck-core-profile/tck-core-profile-test/pom.xml index 4d7f29d938d..15ebe4081bd 100644 --- a/microprofile/tests/tck/tck-core-profile/tck-core-profile-test/pom.xml +++ b/microprofile/tests/tck/tck-core-profile/tck-core-profile-test/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-core-profile - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-tck-core-profile-test diff --git a/microprofile/tests/tck/tck-fault-tolerance/pom.xml b/microprofile/tests/tck/tck-fault-tolerance/pom.xml index 034c0d49fc6..51b99280eaa 100644 --- a/microprofile/tests/tck/tck-fault-tolerance/pom.xml +++ b/microprofile/tests/tck/tck-fault-tolerance/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-fault-tolerance diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml index e5646283b49..aadec9cb895 100644 --- a/microprofile/tests/tck/tck-graphql/pom.xml +++ b/microprofile/tests/tck/tck-graphql/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-graphql diff --git a/microprofile/tests/tck/tck-health/pom.xml b/microprofile/tests/tck/tck-health/pom.xml index 0d02c53f509..55bb975475b 100644 --- a/microprofile/tests/tck/tck-health/pom.xml +++ b/microprofile/tests/tck/tck-health/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/tck/tck-inject/pom.xml b/microprofile/tests/tck/tck-inject/pom.xml index f904a2fde22..ed69b990668 100644 --- a/microprofile/tests/tck/tck-inject/pom.xml +++ b/microprofile/tests/tck/tck-inject/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/tests/tck/tck-inject/tck-inject-test/pom.xml b/microprofile/tests/tck/tck-inject/tck-inject-test/pom.xml index 699b31d76ed..37eaebce31c 100644 --- a/microprofile/tests/tck/tck-inject/tck-inject-test/pom.xml +++ b/microprofile/tests/tck/tck-inject/tck-inject-test/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-inject - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-tck-inject-test diff --git a/microprofile/tests/tck/tck-jsonb/pom.xml b/microprofile/tests/tck/tck-jsonb/pom.xml index dde592ef345..0fa0c737b59 100644 --- a/microprofile/tests/tck/tck-jsonb/pom.xml +++ b/microprofile/tests/tck/tck-jsonb/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/tests/tck/tck-jsonb/tck-jsonb-test/pom.xml b/microprofile/tests/tck/tck-jsonb/tck-jsonb-test/pom.xml index cc09a18707a..4d1ace0878b 100644 --- a/microprofile/tests/tck/tck-jsonb/tck-jsonb-test/pom.xml +++ b/microprofile/tests/tck/tck-jsonb/tck-jsonb-test/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-jsonb - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-tck-jsonb-test diff --git a/microprofile/tests/tck/tck-jsonp/pom.xml b/microprofile/tests/tck/tck-jsonp/pom.xml index 92e62931c78..7d306728868 100644 --- a/microprofile/tests/tck/tck-jsonp/pom.xml +++ b/microprofile/tests/tck/tck-jsonp/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/tests/tck/tck-jsonp/tck-jsonp-pluggability-test/pom.xml b/microprofile/tests/tck/tck-jsonp/tck-jsonp-pluggability-test/pom.xml index 8f92336c2ef..ffb0c463be2 100644 --- a/microprofile/tests/tck/tck-jsonp/tck-jsonp-pluggability-test/pom.xml +++ b/microprofile/tests/tck/tck-jsonp/tck-jsonp-pluggability-test/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-jsonp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-tck-jsonp-pluggability-test diff --git a/microprofile/tests/tck/tck-jsonp/tck-jsonp-test/pom.xml b/microprofile/tests/tck/tck-jsonp/tck-jsonp-test/pom.xml index 1c06657f571..2c538131741 100644 --- a/microprofile/tests/tck/tck-jsonp/tck-jsonp-test/pom.xml +++ b/microprofile/tests/tck/tck-jsonp/tck-jsonp-test/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-jsonp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-tck-jsonp-test diff --git a/microprofile/tests/tck/tck-jwt-auth/pom.xml b/microprofile/tests/tck/tck-jwt-auth/pom.xml index 84a8d88b76d..4cb19d3972b 100644 --- a/microprofile/tests/tck/tck-jwt-auth/pom.xml +++ b/microprofile/tests/tck/tck-jwt-auth/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-jwt-auth diff --git a/microprofile/tests/tck/tck-lra/pom.xml b/microprofile/tests/tck/tck-lra/pom.xml index 1d69bc1b7dc..9cf269ea8e3 100644 --- a/microprofile/tests/tck/tck-lra/pom.xml +++ b/microprofile/tests/tck/tck-lra/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/microprofile/tests/tck/tck-messaging/pom.xml b/microprofile/tests/tck/tck-messaging/pom.xml index 95e2bfdd0bc..ce330578bc8 100644 --- a/microprofile/tests/tck/tck-messaging/pom.xml +++ b/microprofile/tests/tck/tck-messaging/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-messaging diff --git a/microprofile/tests/tck/tck-metrics/pom.xml b/microprofile/tests/tck/tck-metrics/pom.xml index 650fc40c83e..132edc07d8a 100644 --- a/microprofile/tests/tck/tck-metrics/pom.xml +++ b/microprofile/tests/tck/tck-metrics/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-metrics diff --git a/microprofile/tests/tck/tck-openapi/pom.xml b/microprofile/tests/tck/tck-openapi/pom.xml index 164971a5a47..3004f16fbca 100644 --- a/microprofile/tests/tck/tck-openapi/pom.xml +++ b/microprofile/tests/tck/tck-openapi/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/tck/tck-opentracing/pom.xml b/microprofile/tests/tck/tck-opentracing/pom.xml index 5f6af7fd8b7..64595753443 100644 --- a/microprofile/tests/tck/tck-opentracing/pom.xml +++ b/microprofile/tests/tck/tck-opentracing/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-opentracing diff --git a/microprofile/tests/tck/tck-reactive-operators/pom.xml b/microprofile/tests/tck/tck-reactive-operators/pom.xml index 0248067ff82..fcb2b642161 100644 --- a/microprofile/tests/tck/tck-reactive-operators/pom.xml +++ b/microprofile/tests/tck/tck-reactive-operators/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml tck-reactive-operators diff --git a/microprofile/tests/tck/tck-rest-client/pom.xml b/microprofile/tests/tck/tck-rest-client/pom.xml index 8b05658e3b6..0eced978d18 100644 --- a/microprofile/tests/tck/tck-rest-client/pom.xml +++ b/microprofile/tests/tck/tck-rest-client/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/tck/tck-restful/pom.xml b/microprofile/tests/tck/tck-restful/pom.xml index 7614b371bdd..e1a4be0be9a 100644 --- a/microprofile/tests/tck/tck-restful/pom.xml +++ b/microprofile/tests/tck/tck-restful/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/microprofile/tests/tck/tck-restful/tck-restful-test/pom.xml b/microprofile/tests/tck/tck-restful/tck-restful-test/pom.xml index 74079a821ac..2d2317846e4 100644 --- a/microprofile/tests/tck/tck-restful/tck-restful-test/pom.xml +++ b/microprofile/tests/tck/tck-restful/tck-restful-test/pom.xml @@ -24,7 +24,7 @@ io.helidon.microprofile.tests.tck tck-restful - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-microprofile-tests-tck-restful-test diff --git a/microprofile/tests/tck/tck-telemetry/pom.xml b/microprofile/tests/tck/tck-telemetry/pom.xml index a16b01271d1..c80bf114bbf 100644 --- a/microprofile/tests/tck/tck-telemetry/pom.xml +++ b/microprofile/tests/tck/tck-telemetry/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests.tck helidon-microprofile-tests-tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/telemetry/pom.xml b/microprofile/tests/telemetry/pom.xml index 43bbd8a84d4..c5d697420e6 100644 --- a/microprofile/tests/telemetry/pom.xml +++ b/microprofile/tests/telemetry/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests helidon-microprofile-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/microprofile/tests/testing/junit5/pom.xml b/microprofile/tests/testing/junit5/pom.xml index 88ff79a886b..fb83dad7b93 100644 --- a/microprofile/tests/testing/junit5/pom.xml +++ b/microprofile/tests/testing/junit5/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile.tests.testing helidon-microprofile-tests-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/microprofile/tests/testing/pom.xml b/microprofile/tests/testing/pom.xml index e9289939b7e..c81000a1406 100644 --- a/microprofile/tests/testing/pom.xml +++ b/microprofile/tests/testing/pom.xml @@ -23,10 +23,10 @@ io.helidon.microprofile.tests helidon-microprofile-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom io.helidon.microprofile.tests.testing diff --git a/microprofile/tests/testing/testng/pom.xml b/microprofile/tests/testing/testng/pom.xml index 57e0332df7f..a1d7478f6a8 100644 --- a/microprofile/tests/testing/testng/pom.xml +++ b/microprofile/tests/testing/testng/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests.testing helidon-microprofile-tests-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/microprofile/tracing/pom.xml b/microprofile/tracing/pom.xml index 7a4717e1de8..cc4b911e5b8 100644 --- a/microprofile/tracing/pom.xml +++ b/microprofile/tracing/pom.xml @@ -22,7 +22,7 @@ helidon-microprofile-project io.helidon.microprofile - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.tracing helidon-microprofile-tracing diff --git a/microprofile/websocket/pom.xml b/microprofile/websocket/pom.xml index 79ecbcc4178..a0972b0188f 100644 --- a/microprofile/websocket/pom.xml +++ b/microprofile/websocket/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.microprofile.websocket diff --git a/microprofile/weld/pom.xml b/microprofile/weld/pom.xml index e95b6dd8f52..46835c5d20d 100644 --- a/microprofile/weld/pom.xml +++ b/microprofile/weld/pom.xml @@ -22,7 +22,7 @@ io.helidon.microprofile helidon-microprofile-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/weld/weld-core-impl/pom.xml b/microprofile/weld/weld-core-impl/pom.xml index 12ba3c23ef2..db29d5db138 100644 --- a/microprofile/weld/weld-core-impl/pom.xml +++ b/microprofile/weld/weld-core-impl/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.weld helidon-microprofile-weld-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/microprofile/weld/weld-se-core/pom.xml b/microprofile/weld/weld-se-core/pom.xml index 905c1703298..0d615ca9796 100644 --- a/microprofile/weld/weld-se-core/pom.xml +++ b/microprofile/weld/weld-se-core/pom.xml @@ -23,7 +23,7 @@ io.helidon.microprofile.weld helidon-microprofile-weld-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/openapi/openapi/pom.xml b/openapi/openapi/pom.xml index 83a21fb752f..9edaad70a5d 100644 --- a/openapi/openapi/pom.xml +++ b/openapi/openapi/pom.xml @@ -21,7 +21,7 @@ io.helidon.openapi helidon-openapi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-openapi diff --git a/openapi/pom.xml b/openapi/pom.xml index 33cc7e84453..09b56ad512e 100644 --- a/openapi/pom.xml +++ b/openapi/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.openapi diff --git a/openapi/tests/gh-5792/pom.xml b/openapi/tests/gh-5792/pom.xml index 44b4103f03c..10215aa64ee 100644 --- a/openapi/tests/gh-5792/pom.xml +++ b/openapi/tests/gh-5792/pom.xml @@ -24,7 +24,7 @@ io.helidon.openapi.tests helidon-openapi-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-openapi-tests-yaml-parsing diff --git a/openapi/tests/pom.xml b/openapi/tests/pom.xml index 4dc662e76dc..869e0131cce 100644 --- a/openapi/tests/pom.xml +++ b/openapi/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.openapi helidon-openapi-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.openapi.tests diff --git a/parent/pom.xml b/parent/pom.xml index 3d04882637d..92c4f131e8e 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -22,7 +22,7 @@ 4.0.0 io.helidon helidon-parent - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom Helidon Parent diff --git a/pom.xml b/pom.xml index bc27020a09a..d0c7a0260d9 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ io.helidon helidon-dependencies - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ./dependencies/pom.xml helidon-project diff --git a/scheduling/pom.xml b/scheduling/pom.xml index 87eb8ac75e2..e05c3c588b0 100644 --- a/scheduling/pom.xml +++ b/scheduling/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.scheduling diff --git a/security/abac/policy-el/pom.xml b/security/abac/policy-el/pom.xml index 316d1c461af..ef4447f0583 100644 --- a/security/abac/policy-el/pom.xml +++ b/security/abac/policy-el/pom.xml @@ -23,7 +23,7 @@ io.helidon.security.abac helidon-security-abac-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 helidon-security-abac-policy-el diff --git a/security/abac/policy/pom.xml b/security/abac/policy/pom.xml index 8f1fc62ed7a..1bddf59134a 100644 --- a/security/abac/policy/pom.xml +++ b/security/abac/policy/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.abac helidon-security-abac-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-abac-policy Helidon Security Validators Policy diff --git a/security/abac/pom.xml b/security/abac/pom.xml index 221a88df3fb..28c7b109615 100644 --- a/security/abac/pom.xml +++ b/security/abac/pom.xml @@ -23,7 +23,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/security/abac/role/pom.xml b/security/abac/role/pom.xml index d8ee72f7805..d4b02529247 100644 --- a/security/abac/role/pom.xml +++ b/security/abac/role/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.abac helidon-security-abac-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-abac-role Helidon Security Validators Role diff --git a/security/abac/scope/pom.xml b/security/abac/scope/pom.xml index cac4e8bed15..03283e78cfe 100644 --- a/security/abac/scope/pom.xml +++ b/security/abac/scope/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.abac helidon-security-abac-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-abac-scope Helidon Security Validators Scope diff --git a/security/abac/time/pom.xml b/security/abac/time/pom.xml index d9f4ae7a967..8d3a3a963f2 100644 --- a/security/abac/time/pom.xml +++ b/security/abac/time/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.abac helidon-security-abac-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-abac-time Helidon Security Validators Time diff --git a/security/annotations/pom.xml b/security/annotations/pom.xml index a43c28347aa..9fd32380ae1 100644 --- a/security/annotations/pom.xml +++ b/security/annotations/pom.xml @@ -24,7 +24,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-annotations Helidon Security Integration Annotations diff --git a/security/integration/common/pom.xml b/security/integration/common/pom.xml index 96a39a0933d..dec522d9bf1 100644 --- a/security/integration/common/pom.xml +++ b/security/integration/common/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.integration helidon-security-integration-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-integration-common Helidon Security Integration Common diff --git a/security/integration/pom.xml b/security/integration/pom.xml index c6a814a544d..a5fac28050d 100644 --- a/security/integration/pom.xml +++ b/security/integration/pom.xml @@ -24,7 +24,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/security/jwt/pom.xml b/security/jwt/pom.xml index e2f96025f53..5d7eb057e29 100644 --- a/security/jwt/pom.xml +++ b/security/jwt/pom.xml @@ -24,7 +24,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-jwt Helidon Security JWT diff --git a/security/pom.xml b/security/pom.xml index c020daf76fc..43e3970c756 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.security diff --git a/security/providers/abac/pom.xml b/security/providers/abac/pom.xml index 97fd67f97b7..671352d313b 100644 --- a/security/providers/abac/pom.xml +++ b/security/providers/abac/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-abac Helidon Security Providers ABAC diff --git a/security/providers/common/pom.xml b/security/providers/common/pom.xml index d8327a80000..f546c916b68 100644 --- a/security/providers/common/pom.xml +++ b/security/providers/common/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-common Helidon Security Providers Common diff --git a/security/providers/config-vault/pom.xml b/security/providers/config-vault/pom.xml index d7057bbfa95..1f5714191df 100644 --- a/security/providers/config-vault/pom.xml +++ b/security/providers/config-vault/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-config-vault Helidon Security Providers Config Vault diff --git a/security/providers/google-login/pom.xml b/security/providers/google-login/pom.xml index 48b0f0771a5..8417bb2c56f 100644 --- a/security/providers/google-login/pom.xml +++ b/security/providers/google-login/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-google-login Helidon Security Providers Google Login diff --git a/security/providers/header/pom.xml b/security/providers/header/pom.xml index 836ede2e897..06db66e1e63 100644 --- a/security/providers/header/pom.xml +++ b/security/providers/header/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-header Helidon Security Providers Header authentication diff --git a/security/providers/http-auth/pom.xml b/security/providers/http-auth/pom.xml index f41de46d547..3357a2f096d 100644 --- a/security/providers/http-auth/pom.xml +++ b/security/providers/http-auth/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-http-auth Helidon Security Providers HTTP Authentication diff --git a/security/providers/http-sign/pom.xml b/security/providers/http-sign/pom.xml index b64e759e8ee..173cc90b286 100644 --- a/security/providers/http-sign/pom.xml +++ b/security/providers/http-sign/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-http-sign Helidon Security Providers HTTP Signature diff --git a/security/providers/idcs-mapper/pom.xml b/security/providers/idcs-mapper/pom.xml index 526d4bba1d4..3c848a3555b 100644 --- a/security/providers/idcs-mapper/pom.xml +++ b/security/providers/idcs-mapper/pom.xml @@ -21,7 +21,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/security/providers/jwt/pom.xml b/security/providers/jwt/pom.xml index 3a42867c78a..275440b1e47 100644 --- a/security/providers/jwt/pom.xml +++ b/security/providers/jwt/pom.xml @@ -24,7 +24,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-providers-jwt Helidon Security Providers JWT diff --git a/security/providers/oidc-common/pom.xml b/security/providers/oidc-common/pom.xml index 180d66c7111..24baca678d1 100644 --- a/security/providers/oidc-common/pom.xml +++ b/security/providers/oidc-common/pom.xml @@ -21,7 +21,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 helidon-security-providers-oidc-common diff --git a/security/providers/oidc/pom.xml b/security/providers/oidc/pom.xml index 7232ebc81af..19817daff75 100644 --- a/security/providers/oidc/pom.xml +++ b/security/providers/oidc/pom.xml @@ -23,7 +23,7 @@ io.helidon.security.providers helidon-security-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 helidon-security-providers-oidc diff --git a/security/providers/pom.xml b/security/providers/pom.xml index ada33c79ddb..3aa2479302e 100644 --- a/security/providers/pom.xml +++ b/security/providers/pom.xml @@ -24,7 +24,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.security.providers helidon-security-providers-project diff --git a/security/security/pom.xml b/security/security/pom.xml index c993a4a7e3d..06d50e84a56 100644 --- a/security/security/pom.xml +++ b/security/security/pom.xml @@ -24,7 +24,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security Helidon Security diff --git a/security/util/pom.xml b/security/util/pom.xml index 62a8b276dbd..ae0658e6fc9 100644 --- a/security/util/pom.xml +++ b/security/util/pom.xml @@ -24,7 +24,7 @@ io.helidon.security helidon-security-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-security-util Helidon Security Utilities diff --git a/service/codegen/pom.xml b/service/codegen/pom.xml index ce6fe0e463a..48c5305d120 100644 --- a/service/codegen/pom.xml +++ b/service/codegen/pom.xml @@ -23,7 +23,7 @@ io.helidon.service helidon-service-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/service/pom.xml b/service/pom.xml index 412833b6902..9efc2d258e3 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.service diff --git a/service/registry/pom.xml b/service/registry/pom.xml index 0e9379c3835..feca32208ed 100644 --- a/service/registry/pom.xml +++ b/service/registry/pom.xml @@ -23,7 +23,7 @@ io.helidon.service helidon-service-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/service/tests/codegen/pom.xml b/service/tests/codegen/pom.xml index 735ed76d54a..15b5fe321a1 100644 --- a/service/tests/codegen/pom.xml +++ b/service/tests/codegen/pom.xml @@ -23,7 +23,7 @@ io.helidon.service.tests helidon-service-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/service/tests/pom.xml b/service/tests/pom.xml index 8c3134847f3..447f0026ac1 100644 --- a/service/tests/pom.xml +++ b/service/tests/pom.xml @@ -22,7 +22,7 @@ io.helidon.service helidon-service-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/service/tests/registry/pom.xml b/service/tests/registry/pom.xml index 3ab2a699224..f13b903e646 100644 --- a/service/tests/registry/pom.xml +++ b/service/tests/registry/pom.xml @@ -22,7 +22,7 @@ io.helidon.service.tests helidon-service-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/tests/apps/bookstore/bookstore-mp/pom.xml b/tests/apps/bookstore/bookstore-mp/pom.xml index 05ef639e659..f3e868178e0 100644 --- a/tests/apps/bookstore/bookstore-mp/pom.xml +++ b/tests/apps/bookstore/bookstore-mp/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.tests.apps.bookstore.bookstore-mp diff --git a/tests/apps/bookstore/bookstore-se/pom.xml b/tests/apps/bookstore/bookstore-se/pom.xml index dff22b62148..24ae2da6f4c 100644 --- a/tests/apps/bookstore/bookstore-se/pom.xml +++ b/tests/apps/bookstore/bookstore-se/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml io.helidon.tests.apps.bookstore.bookstore-se diff --git a/tests/apps/bookstore/common/pom.xml b/tests/apps/bookstore/common/pom.xml index 0da153409f4..b25cd0f314c 100644 --- a/tests/apps/bookstore/common/pom.xml +++ b/tests/apps/bookstore/common/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml io.helidon.tests.apps.bookstore.common diff --git a/tests/apps/bookstore/pom.xml b/tests/apps/bookstore/pom.xml index 2aa1db82e75..994e284190a 100644 --- a/tests/apps/bookstore/pom.xml +++ b/tests/apps/bookstore/pom.xml @@ -24,7 +24,7 @@ io.helidon.tests.apps helidon-tests-apps-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.apps.bookstore diff --git a/tests/apps/pom.xml b/tests/apps/pom.xml index 943af805200..db0b15e9577 100644 --- a/tests/apps/pom.xml +++ b/tests/apps/pom.xml @@ -24,7 +24,7 @@ io.helidon.tests helidon-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.apps diff --git a/tests/benchmark/jmh/pom.xml b/tests/benchmark/jmh/pom.xml index 40f2304d33a..ad05c95f711 100644 --- a/tests/benchmark/jmh/pom.xml +++ b/tests/benchmark/jmh/pom.xml @@ -21,7 +21,7 @@ io.helidon.tests.benchmark helidon-tests-benchmark-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-benchmark-jmh diff --git a/tests/benchmark/pom.xml b/tests/benchmark/pom.xml index 974accb9a63..0120617e48d 100644 --- a/tests/benchmark/pom.xml +++ b/tests/benchmark/pom.xml @@ -21,7 +21,7 @@ io.helidon.tests helidon-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.benchmark diff --git a/tests/functional/bookstore/pom.xml b/tests/functional/bookstore/pom.xml index 56ff73e464e..84c8e77108a 100644 --- a/tests/functional/bookstore/pom.xml +++ b/tests/functional/bookstore/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.functional helidon-tests-functional-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.functional.bookstore helidon-tests-functional-bookstore diff --git a/tests/functional/config-profiles/pom.xml b/tests/functional/config-profiles/pom.xml index 2176a34872d..5f93dadd05b 100644 --- a/tests/functional/config-profiles/pom.xml +++ b/tests/functional/config-profiles/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.functional helidon-tests-functional-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.functional.configprofile helidon-tests-functional-configprofile diff --git a/tests/functional/context-propagation/pom.xml b/tests/functional/context-propagation/pom.xml index 8a1c8ddd138..ca1caa7158f 100644 --- a/tests/functional/context-propagation/pom.xml +++ b/tests/functional/context-propagation/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-context-propagation diff --git a/tests/functional/jax-rs-multiple-apps/pom.xml b/tests/functional/jax-rs-multiple-apps/pom.xml index b2550bb6ed9..d02e65b8ebd 100644 --- a/tests/functional/jax-rs-multiple-apps/pom.xml +++ b/tests/functional/jax-rs-multiple-apps/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-jax-rs-multiple-apps diff --git a/tests/functional/jax-rs-subresource/pom.xml b/tests/functional/jax-rs-subresource/pom.xml index bd6191a7231..1ce66bc7b29 100644 --- a/tests/functional/jax-rs-subresource/pom.xml +++ b/tests/functional/jax-rs-subresource/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-jax-rs-subresource Helidon Tests Functional JAX-RS Subresources diff --git a/tests/functional/mp-compression/pom.xml b/tests/functional/mp-compression/pom.xml index 6373f17a427..6930d5c769f 100644 --- a/tests/functional/mp-compression/pom.xml +++ b/tests/functional/mp-compression/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-mp-compression Helidon Tests Functional HTTP compression diff --git a/tests/functional/mp-synthetic-app/pom.xml b/tests/functional/mp-synthetic-app/pom.xml index eb4ff8d0cb8..a9a3eb26218 100644 --- a/tests/functional/mp-synthetic-app/pom.xml +++ b/tests/functional/mp-synthetic-app/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-mp-synthetic-app Helidon Tests Functional MP Synthetic Application diff --git a/tests/functional/multiport/pom.xml b/tests/functional/multiport/pom.xml index dafb3bcbe8e..eda9c7315ae 100644 --- a/tests/functional/multiport/pom.xml +++ b/tests/functional/multiport/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-multiport Helidon Tests Functional Multiport with MP diff --git a/tests/functional/param-converter-provider/pom.xml b/tests/functional/param-converter-provider/pom.xml index c22e428fa6f..6caaaab9ea5 100644 --- a/tests/functional/param-converter-provider/pom.xml +++ b/tests/functional/param-converter-provider/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-param-converter-provider diff --git a/tests/functional/pom.xml b/tests/functional/pom.xml index bee41013c7d..f0a5e00aa14 100644 --- a/tests/functional/pom.xml +++ b/tests/functional/pom.xml @@ -24,7 +24,7 @@ io.helidon.tests helidon-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.functional diff --git a/tests/functional/request-scope-cdi/pom.xml b/tests/functional/request-scope-cdi/pom.xml index d75601fdbb2..b41855dbfab 100644 --- a/tests/functional/request-scope-cdi/pom.xml +++ b/tests/functional/request-scope-cdi/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-request-scope-cdi diff --git a/tests/functional/request-scope-injection/pom.xml b/tests/functional/request-scope-injection/pom.xml index a0bedc63df9..d319fbfc610 100644 --- a/tests/functional/request-scope-injection/pom.xml +++ b/tests/functional/request-scope-injection/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-request-scope-injection diff --git a/tests/functional/request-scope/pom.xml b/tests/functional/request-scope/pom.xml index fd6c929f2e3..a954602a778 100644 --- a/tests/functional/request-scope/pom.xml +++ b/tests/functional/request-scope/pom.xml @@ -22,7 +22,7 @@ helidon-tests-functional-project io.helidon.tests.functional - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-functional-request-scope diff --git a/tests/integration/config/gh-2171-yml/pom.xml b/tests/integration/config/gh-2171-yml/pom.xml index 6b4ff9325da..cc8cf601c64 100644 --- a/tests/integration/config/gh-2171-yml/pom.xml +++ b/tests/integration/config/gh-2171-yml/pom.xml @@ -20,7 +20,7 @@ io.helidon.tests.integration helidon-tests-integration-config - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/config/gh-2171/pom.xml b/tests/integration/config/gh-2171/pom.xml index d7a70c0e00d..2c0dc8129e0 100644 --- a/tests/integration/config/gh-2171/pom.xml +++ b/tests/integration/config/gh-2171/pom.xml @@ -20,7 +20,7 @@ io.helidon.tests.integration helidon-tests-integration-config - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/config/gh-4375/pom.xml b/tests/integration/config/gh-4375/pom.xml index 84769c0ab4d..87556183304 100644 --- a/tests/integration/config/gh-4375/pom.xml +++ b/tests/integration/config/gh-4375/pom.xml @@ -24,7 +24,7 @@ io.helidon.tests.integration helidon-tests-integration-config - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-config-gh-4375 diff --git a/tests/integration/config/hocon-mp/pom.xml b/tests/integration/config/hocon-mp/pom.xml index 9aa554ef506..1f70b267cd3 100644 --- a/tests/integration/config/hocon-mp/pom.xml +++ b/tests/integration/config/hocon-mp/pom.xml @@ -24,7 +24,7 @@ io.helidon.tests.integration helidon-tests-integration-config - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-config-hocon-mp diff --git a/tests/integration/config/pom.xml b/tests/integration/config/pom.xml index 8f25207aa5e..ca66ff4214e 100644 --- a/tests/integration/config/pom.xml +++ b/tests/integration/config/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/tests/integration/dbclient/app/pom.xml b/tests/integration/dbclient/app/pom.xml index e8eebda21bf..0b01742c984 100644 --- a/tests/integration/dbclient/app/pom.xml +++ b/tests/integration/dbclient/app/pom.xml @@ -21,12 +21,12 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml io.helidon.tests.integration.dbclient helidon-tests-integration-dbclient-app - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT Helidon Tests Integration Database Client Application diff --git a/tests/integration/dbclient/common/pom.xml b/tests/integration/dbclient/common/pom.xml index aec8eae0d9f..a09d1f0565e 100644 --- a/tests/integration/dbclient/common/pom.xml +++ b/tests/integration/dbclient/common/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.dbclient helidon-tests-integration-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-tests-integration-dbclient-common diff --git a/tests/integration/dbclient/h2/pom.xml b/tests/integration/dbclient/h2/pom.xml index 4e185b00980..ea1cdb163c5 100644 --- a/tests/integration/dbclient/h2/pom.xml +++ b/tests/integration/dbclient/h2/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration.dbclient helidon-tests-integration-dbclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml helidon-tests-integration-dbclient-h2 diff --git a/tests/integration/dbclient/pom.xml b/tests/integration/dbclient/pom.xml index ee2f6b46711..651b8247354 100644 --- a/tests/integration/dbclient/pom.xml +++ b/tests/integration/dbclient/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml io.helidon.tests.integration.dbclient diff --git a/tests/integration/harness/pom.xml b/tests/integration/harness/pom.xml index 673118fcdac..39f4843e2ad 100644 --- a/tests/integration/harness/pom.xml +++ b/tests/integration/harness/pom.xml @@ -20,7 +20,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 helidon-tests-integration-harness diff --git a/tests/integration/health/mp-disabled/pom.xml b/tests/integration/health/mp-disabled/pom.xml index 28b919d79fa..1507c478c44 100644 --- a/tests/integration/health/mp-disabled/pom.xml +++ b/tests/integration/health/mp-disabled/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.tests.integration.health diff --git a/tests/integration/health/pom.xml b/tests/integration/health/pom.xml index 18d4c7a02d7..74e67956d8d 100644 --- a/tests/integration/health/pom.xml +++ b/tests/integration/health/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.integration.health helidon-tests-integration-health-project diff --git a/tests/integration/jep290/check_f_f_ok/pom.xml b/tests/integration/jep290/check_f_f_ok/pom.xml index cef2812f25e..643232b0cb6 100644 --- a/tests/integration/jep290/check_f_f_ok/pom.xml +++ b/tests/integration/jep290/check_f_f_ok/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-check_f_f_ok diff --git a/tests/integration/jep290/check_f_f_w/pom.xml b/tests/integration/jep290/check_f_f_w/pom.xml index 93cef9373d3..1c3e2f5a85a 100644 --- a/tests/integration/jep290/check_f_f_w/pom.xml +++ b/tests/integration/jep290/check_f_f_w/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-check_f_f_w diff --git a/tests/integration/jep290/check_f_p_ok/pom.xml b/tests/integration/jep290/check_f_p_ok/pom.xml index 2fe8c1e9b04..4a98bf1ef2d 100644 --- a/tests/integration/jep290/check_f_p_ok/pom.xml +++ b/tests/integration/jep290/check_f_p_ok/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-check_f_p_ok diff --git a/tests/integration/jep290/check_f_p_w/pom.xml b/tests/integration/jep290/check_f_p_w/pom.xml index 6816efe2457..a5cf3c15f6c 100644 --- a/tests/integration/jep290/check_f_p_w/pom.xml +++ b/tests/integration/jep290/check_f_p_w/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-check_f_p_w diff --git a/tests/integration/jep290/pom.xml b/tests/integration/jep290/pom.xml index a073f1aeaaf..e6f7110ba29 100644 --- a/tests/integration/jep290/pom.xml +++ b/tests/integration/jep290/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project diff --git a/tests/integration/jep290/server_and_custom/pom.xml b/tests/integration/jep290/server_and_custom/pom.xml index ad58a70401c..12cc6f0e677 100644 --- a/tests/integration/jep290/server_and_custom/pom.xml +++ b/tests/integration/jep290/server_and_custom/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-server-and-custom diff --git a/tests/integration/jep290/set_c_f_c/pom.xml b/tests/integration/jep290/set_c_f_c/pom.xml index e93df3e3598..5f4fe19cd29 100644 --- a/tests/integration/jep290/set_c_f_c/pom.xml +++ b/tests/integration/jep290/set_c_f_c/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-set_c_f_c diff --git a/tests/integration/jep290/set_c_f_d/pom.xml b/tests/integration/jep290/set_c_f_d/pom.xml index 4ef8088b677..21a6f0a86f9 100644 --- a/tests/integration/jep290/set_c_f_d/pom.xml +++ b/tests/integration/jep290/set_c_f_d/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-set_c_f_d diff --git a/tests/integration/jep290/set_c_t_d/pom.xml b/tests/integration/jep290/set_c_t_d/pom.xml index 4e4e7617fdf..26b3fe28567 100644 --- a/tests/integration/jep290/set_c_t_d/pom.xml +++ b/tests/integration/jep290/set_c_t_d/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-set_c_t_d diff --git a/tests/integration/jep290/set_f/pom.xml b/tests/integration/jep290/set_f/pom.xml index 7b6762f995e..3a49deb0bd2 100644 --- a/tests/integration/jep290/set_f/pom.xml +++ b/tests/integration/jep290/set_f/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-set_f diff --git a/tests/integration/jep290/set_o/pom.xml b/tests/integration/jep290/set_o/pom.xml index efde86a28d5..6eb3b00c51a 100644 --- a/tests/integration/jep290/set_o/pom.xml +++ b/tests/integration/jep290/set_o/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jep290 helidon-tests-integration-jep290-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-jep290-set_o diff --git a/tests/integration/jms/pom.xml b/tests/integration/jms/pom.xml index 91240de7d07..b57db082739 100644 --- a/tests/integration/jms/pom.xml +++ b/tests/integration/jms/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.integration.jms diff --git a/tests/integration/jpa/appl/pom.xml b/tests/integration/jpa/appl/pom.xml index 3e0154fa0fe..cbdb2cb2c2a 100644 --- a/tests/integration/jpa/appl/pom.xml +++ b/tests/integration/jpa/appl/pom.xml @@ -23,7 +23,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml diff --git a/tests/integration/jpa/model/pom.xml b/tests/integration/jpa/model/pom.xml index 9ebab6dcdde..aa4fac410be 100644 --- a/tests/integration/jpa/model/pom.xml +++ b/tests/integration/jpa/model/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jpa helidon-tests-integration-jpa-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/tests/integration/jpa/pom.xml b/tests/integration/jpa/pom.xml index 5c693755272..c7562079b27 100644 --- a/tests/integration/jpa/pom.xml +++ b/tests/integration/jpa/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/tests/integration/jpa/simple/pom.xml b/tests/integration/jpa/simple/pom.xml index 21a62ab4755..4a2a47d241f 100644 --- a/tests/integration/jpa/simple/pom.xml +++ b/tests/integration/jpa/simple/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration.jpa helidon-tests-integration-jpa-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/tests/integration/kafka/pom.xml b/tests/integration/kafka/pom.xml index dd159d99779..69cd54271b1 100644 --- a/tests/integration/kafka/pom.xml +++ b/tests/integration/kafka/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.integration.kafka diff --git a/tests/integration/mp-bean-validation/pom.xml b/tests/integration/mp-bean-validation/pom.xml index 25e6b67aecc..1745ac6f824 100644 --- a/tests/integration/mp-bean-validation/pom.xml +++ b/tests/integration/mp-bean-validation/pom.xml @@ -24,7 +24,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.tests.integration diff --git a/tests/integration/mp-gh-2421/pom.xml b/tests/integration/mp-gh-2421/pom.xml index 55e9e9dc53c..4bd9543b382 100644 --- a/tests/integration/mp-gh-2421/pom.xml +++ b/tests/integration/mp-gh-2421/pom.xml @@ -22,7 +22,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-2461/pom.xml b/tests/integration/mp-gh-2461/pom.xml index 96912a7c27b..e3bd904471f 100644 --- a/tests/integration/mp-gh-2461/pom.xml +++ b/tests/integration/mp-gh-2461/pom.xml @@ -22,7 +22,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-3246/pom.xml b/tests/integration/mp-gh-3246/pom.xml index 942a7bc4420..2c5cc667f1f 100644 --- a/tests/integration/mp-gh-3246/pom.xml +++ b/tests/integration/mp-gh-3246/pom.xml @@ -22,7 +22,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-3974/pom.xml b/tests/integration/mp-gh-3974/pom.xml index 30f97e50317..393dd8cb6c2 100644 --- a/tests/integration/mp-gh-3974/pom.xml +++ b/tests/integration/mp-gh-3974/pom.xml @@ -22,7 +22,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-4123/pom.xml b/tests/integration/mp-gh-4123/pom.xml index e576871ee80..2f18cae0292 100644 --- a/tests/integration/mp-gh-4123/pom.xml +++ b/tests/integration/mp-gh-4123/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-4654/pom.xml b/tests/integration/mp-gh-4654/pom.xml index 147776be9e4..f27973b91d8 100644 --- a/tests/integration/mp-gh-4654/pom.xml +++ b/tests/integration/mp-gh-4654/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-5328/pom.xml b/tests/integration/mp-gh-5328/pom.xml index 7d32fd5e167..c8d13767399 100644 --- a/tests/integration/mp-gh-5328/pom.xml +++ b/tests/integration/mp-gh-5328/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-8349/pom.xml b/tests/integration/mp-gh-8349/pom.xml index c8693d95fed..82184a80fcc 100644 --- a/tests/integration/mp-gh-8349/pom.xml +++ b/tests/integration/mp-gh-8349/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-8478/pom.xml b/tests/integration/mp-gh-8478/pom.xml index 8764baf608f..d764ab03c83 100644 --- a/tests/integration/mp-gh-8478/pom.xml +++ b/tests/integration/mp-gh-8478/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-8493/pom.xml b/tests/integration/mp-gh-8493/pom.xml index e054c82a765..1f5f6046a42 100644 --- a/tests/integration/mp-gh-8493/pom.xml +++ b/tests/integration/mp-gh-8493/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-gh-8495/pom.xml b/tests/integration/mp-gh-8495/pom.xml index 74081cddfb2..0e8cb78a2ce 100644 --- a/tests/integration/mp-gh-8495/pom.xml +++ b/tests/integration/mp-gh-8495/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/mp-graphql/pom.xml b/tests/integration/mp-graphql/pom.xml index 0ccef460ec4..49b4c77135e 100644 --- a/tests/integration/mp-graphql/pom.xml +++ b/tests/integration/mp-graphql/pom.xml @@ -21,7 +21,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml diff --git a/tests/integration/mp-security-client/pom.xml b/tests/integration/mp-security-client/pom.xml index e2c9ef72b19..bf5a7c3992e 100644 --- a/tests/integration/mp-security-client/pom.xml +++ b/tests/integration/mp-security-client/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.tests.integration diff --git a/tests/integration/mp-ws-services/pom.xml b/tests/integration/mp-ws-services/pom.xml index 8a15eaa8e2c..ffaed72fd09 100644 --- a/tests/integration/mp-ws-services/pom.xml +++ b/tests/integration/mp-ws-services/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.tests.integration diff --git a/tests/integration/native-image/mp-1/pom.xml b/tests/integration/native-image/mp-1/pom.xml index e6265672e8a..c1405fe9ff4 100644 --- a/tests/integration/native-image/mp-1/pom.xml +++ b/tests/integration/native-image/mp-1/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.tests.integration.native-image diff --git a/tests/integration/native-image/mp-2/pom.xml b/tests/integration/native-image/mp-2/pom.xml index 62d211fd3b3..6b9b417cbf3 100644 --- a/tests/integration/native-image/mp-2/pom.xml +++ b/tests/integration/native-image/mp-2/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.tests.integration.native-image diff --git a/tests/integration/native-image/mp-3/pom.xml b/tests/integration/native-image/mp-3/pom.xml index 99adea0f478..597c24f49e0 100644 --- a/tests/integration/native-image/mp-3/pom.xml +++ b/tests/integration/native-image/mp-3/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/mp/pom.xml io.helidon.tests.integration.native-image diff --git a/tests/integration/native-image/pom.xml b/tests/integration/native-image/pom.xml index 1cb754cd193..b9554569dbd 100644 --- a/tests/integration/native-image/pom.xml +++ b/tests/integration/native-image/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../pom.xml pom diff --git a/tests/integration/native-image/se-1/pom.xml b/tests/integration/native-image/se-1/pom.xml index 8c01963eb8c..63293350462 100644 --- a/tests/integration/native-image/se-1/pom.xml +++ b/tests/integration/native-image/se-1/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml io.helidon.tests.integration.native-image diff --git a/tests/integration/native-image/static-content/pom.xml b/tests/integration/native-image/static-content/pom.xml index 2ad628e3c1e..eb56878b61a 100644 --- a/tests/integration/native-image/static-content/pom.xml +++ b/tests/integration/native-image/static-content/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../../applications/se/pom.xml io.helidon.tests.integration diff --git a/tests/integration/oidc/pom.xml b/tests/integration/oidc/pom.xml index 95c59c7b5a0..4012a2a8adb 100644 --- a/tests/integration/oidc/pom.xml +++ b/tests/integration/oidc/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml index 6b13443505f..c2c8b772383 100644 --- a/tests/integration/pom.xml +++ b/tests/integration/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests helidon-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.integration diff --git a/tests/integration/restclient-connector/pom.xml b/tests/integration/restclient-connector/pom.xml index 16c537519dc..f083269752d 100644 --- a/tests/integration/restclient-connector/pom.xml +++ b/tests/integration/restclient-connector/pom.xml @@ -22,7 +22,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-restclient-connector diff --git a/tests/integration/restclient/pom.xml b/tests/integration/restclient/pom.xml index 0860bf84759..bcb054e1829 100644 --- a/tests/integration/restclient/pom.xml +++ b/tests/integration/restclient/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/security/gh1487/pom.xml b/tests/integration/security/gh1487/pom.xml index fec1984d50d..e4d2997f64b 100644 --- a/tests/integration/security/gh1487/pom.xml +++ b/tests/integration/security/gh1487/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration-security io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/security/gh2297/pom.xml b/tests/integration/security/gh2297/pom.xml index ed9c32fa9d1..45feb6d3743 100644 --- a/tests/integration/security/gh2297/pom.xml +++ b/tests/integration/security/gh2297/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration-security io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/security/gh2455/pom.xml b/tests/integration/security/gh2455/pom.xml index c725bd86a11..986da2f83b8 100644 --- a/tests/integration/security/gh2455/pom.xml +++ b/tests/integration/security/gh2455/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration-security io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/security/gh2772/pom.xml b/tests/integration/security/gh2772/pom.xml index 0b29cdecc12..2836a2518e2 100644 --- a/tests/integration/security/gh2772/pom.xml +++ b/tests/integration/security/gh2772/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration-security io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/security/path-params/pom.xml b/tests/integration/security/path-params/pom.xml index eb38a9af511..1dabe3f5406 100644 --- a/tests/integration/security/path-params/pom.xml +++ b/tests/integration/security/path-params/pom.xml @@ -22,7 +22,7 @@ helidon-tests-integration-security io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/security/pom.xml b/tests/integration/security/pom.xml index 5599dd7c5e1..9471887a9f0 100644 --- a/tests/integration/security/pom.xml +++ b/tests/integration/security/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/tests/integration/security/security-annotation/pom.xml b/tests/integration/security/security-annotation/pom.xml index 78961aeffea..a5af6a875a8 100644 --- a/tests/integration/security/security-annotation/pom.xml +++ b/tests/integration/security/security-annotation/pom.xml @@ -21,7 +21,7 @@ io.helidon.tests.integration helidon-tests-integration-security - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT diff --git a/tests/integration/security/security-response-mapper/pom.xml b/tests/integration/security/security-response-mapper/pom.xml index 163af95423c..6091c8d371e 100644 --- a/tests/integration/security/security-response-mapper/pom.xml +++ b/tests/integration/security/security-response-mapper/pom.xml @@ -21,7 +21,7 @@ helidon-tests-integration-security io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/tests/integration/tls-revocation-config/pom.xml b/tests/integration/tls-revocation-config/pom.xml index 0cff77c0731..392c5651826 100644 --- a/tests/integration/tls-revocation-config/pom.xml +++ b/tests/integration/tls-revocation-config/pom.xml @@ -21,7 +21,7 @@ io.helidon.tests.integration helidon-tests-integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-integration-tls-revocation-config diff --git a/tests/integration/vault/pom.xml b/tests/integration/vault/pom.xml index a9065651271..1c1b69f35df 100644 --- a/tests/integration/vault/pom.xml +++ b/tests/integration/vault/pom.xml @@ -23,7 +23,7 @@ helidon-tests-integration io.helidon.tests.integration - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests.integration.vault diff --git a/tests/integration/zipkin-mp-2.2/pom.xml b/tests/integration/zipkin-mp-2.2/pom.xml index 9204c31ad57..9bd447f4499 100644 --- a/tests/integration/zipkin-mp-2.2/pom.xml +++ b/tests/integration/zipkin-mp-2.2/pom.xml @@ -22,7 +22,7 @@ io.helidon.applications helidon-mp - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/mp/pom.xml io.helidon.tests.integration diff --git a/tests/pom.xml b/tests/pom.xml index da01b598edb..7dffe1b5c74 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -23,7 +23,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.tests diff --git a/tests/tck/pom.xml b/tests/tck/pom.xml index 25236239f50..73b3d07505d 100644 --- a/tests/tck/pom.xml +++ b/tests/tck/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests helidon-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 pom diff --git a/tests/tck/tck-reactive-streams/pom.xml b/tests/tck/tck-reactive-streams/pom.xml index 4c5136ebc40..6d7d070a9e7 100644 --- a/tests/tck/tck-reactive-streams/pom.xml +++ b/tests/tck/tck-reactive-streams/pom.xml @@ -23,7 +23,7 @@ io.helidon.tests tck-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 tck-reactive-streams diff --git a/tracing/config/pom.xml b/tracing/config/pom.xml index 9c1d2e9780d..61396dc214c 100644 --- a/tracing/config/pom.xml +++ b/tracing/config/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-config diff --git a/tracing/jersey-client/pom.xml b/tracing/jersey-client/pom.xml index 02df65a1d27..f21a97da106 100644 --- a/tracing/jersey-client/pom.xml +++ b/tracing/jersey-client/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-jersey-client diff --git a/tracing/jersey/pom.xml b/tracing/jersey/pom.xml index 65d7e5df212..9360ebe8fd1 100644 --- a/tracing/jersey/pom.xml +++ b/tracing/jersey/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-jersey diff --git a/tracing/pom.xml b/tracing/pom.xml index 888146bc3df..927e7c8ec75 100644 --- a/tracing/pom.xml +++ b/tracing/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.tracing diff --git a/tracing/provider-tests/pom.xml b/tracing/provider-tests/pom.xml index 6da786e6385..0344d71e8f2 100644 --- a/tracing/provider-tests/pom.xml +++ b/tracing/provider-tests/pom.xml @@ -21,7 +21,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-provider-tests diff --git a/tracing/providers/jaeger/pom.xml b/tracing/providers/jaeger/pom.xml index d02677b8a7e..903fecac641 100644 --- a/tracing/providers/jaeger/pom.xml +++ b/tracing/providers/jaeger/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing.providers helidon-tracing-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-providers-jaeger diff --git a/tracing/providers/opentelemetry/pom.xml b/tracing/providers/opentelemetry/pom.xml index d4bfe8dd53c..18758ae13f1 100644 --- a/tracing/providers/opentelemetry/pom.xml +++ b/tracing/providers/opentelemetry/pom.xml @@ -21,7 +21,7 @@ io.helidon.tracing.providers helidon-tracing-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-providers-opentelemetry diff --git a/tracing/providers/opentracing/pom.xml b/tracing/providers/opentracing/pom.xml index a1d4c65d14b..d2808228b68 100644 --- a/tracing/providers/opentracing/pom.xml +++ b/tracing/providers/opentracing/pom.xml @@ -21,7 +21,7 @@ io.helidon.tracing.providers helidon-tracing-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-providers-opentracing diff --git a/tracing/providers/pom.xml b/tracing/providers/pom.xml index 65a94297128..0d3d4997bde 100644 --- a/tracing/providers/pom.xml +++ b/tracing/providers/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom io.helidon.tracing.providers diff --git a/tracing/providers/zipkin/pom.xml b/tracing/providers/zipkin/pom.xml index 3eb2de5fb47..2d0a706d4ce 100644 --- a/tracing/providers/zipkin/pom.xml +++ b/tracing/providers/zipkin/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing.providers helidon-tracing-providers-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-providers-zipkin diff --git a/tracing/tests/it-tracing-client-zipkin/pom.xml b/tracing/tests/it-tracing-client-zipkin/pom.xml index 0e9390c39e8..12198ea036a 100644 --- a/tracing/tests/it-tracing-client-zipkin/pom.xml +++ b/tracing/tests/it-tracing-client-zipkin/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-tests - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-tests-it1 diff --git a/tracing/tests/pom.xml b/tracing/tests/pom.xml index 5a13792ac39..78f85fd2361 100644 --- a/tracing/tests/pom.xml +++ b/tracing/tests/pom.xml @@ -25,7 +25,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/tracing/tracer-resolver/pom.xml b/tracing/tracer-resolver/pom.xml index 255f9215305..722e0029241 100644 --- a/tracing/tracer-resolver/pom.xml +++ b/tracing/tracer-resolver/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing-tracer-resolver diff --git a/tracing/tracing/pom.xml b/tracing/tracing/pom.xml index 01065335be1..c1cb38f157e 100644 --- a/tracing/tracing/pom.xml +++ b/tracing/tracing/pom.xml @@ -22,7 +22,7 @@ io.helidon.tracing helidon-tracing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tracing diff --git a/webclient/api/pom.xml b/webclient/api/pom.xml index f4083b8d2a7..2848b354a96 100644 --- a/webclient/api/pom.xml +++ b/webclient/api/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-api Helidon WebClient API diff --git a/webclient/dns-resolver/first/pom.xml b/webclient/dns-resolver/first/pom.xml index 76c03bec305..e1ac0ee0398 100644 --- a/webclient/dns-resolver/first/pom.xml +++ b/webclient/dns-resolver/first/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient.dns.resolver helidon-webclient-dns-resolver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-dns-resolver-first diff --git a/webclient/dns-resolver/pom.xml b/webclient/dns-resolver/pom.xml index 512bc51f15e..53694a73527 100644 --- a/webclient/dns-resolver/pom.xml +++ b/webclient/dns-resolver/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/webclient/dns-resolver/round-robin/pom.xml b/webclient/dns-resolver/round-robin/pom.xml index 611b33a23b5..ac887964cf2 100644 --- a/webclient/dns-resolver/round-robin/pom.xml +++ b/webclient/dns-resolver/round-robin/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient.dns.resolver helidon-webclient-dns-resolver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-dns-resolver-round-robin diff --git a/webclient/http1/pom.xml b/webclient/http1/pom.xml index bae4376e42a..9707aabb8fc 100644 --- a/webclient/http1/pom.xml +++ b/webclient/http1/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-http1 diff --git a/webclient/http2/pom.xml b/webclient/http2/pom.xml index 3c32b6859a1..89e331106c2 100644 --- a/webclient/http2/pom.xml +++ b/webclient/http2/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-http2 diff --git a/webclient/metrics/pom.xml b/webclient/metrics/pom.xml index 7f68847c318..114777762fa 100644 --- a/webclient/metrics/pom.xml +++ b/webclient/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-metrics diff --git a/webclient/pom.xml b/webclient/pom.xml index e2ee8899811..e10e2b40719 100644 --- a/webclient/pom.xml +++ b/webclient/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webclient helidon-webclient-project diff --git a/webclient/security/pom.xml b/webclient/security/pom.xml index 914d50a945c..f899a4f1c18 100644 --- a/webclient/security/pom.xml +++ b/webclient/security/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-security diff --git a/webclient/sse/pom.xml b/webclient/sse/pom.xml index 4c5b7cd2e59..89f24ae20bb 100644 --- a/webclient/sse/pom.xml +++ b/webclient/sse/pom.xml @@ -23,7 +23,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-sse diff --git a/webclient/tests/http1/pom.xml b/webclient/tests/http1/pom.xml index ab29b55226c..318419589d8 100644 --- a/webclient/tests/http1/pom.xml +++ b/webclient/tests/http1/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient.tests helidon-webclient-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-tests-http1 diff --git a/webclient/tests/http2/pom.xml b/webclient/tests/http2/pom.xml index 9dc40e04dec..aca82463d1c 100644 --- a/webclient/tests/http2/pom.xml +++ b/webclient/tests/http2/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient.tests helidon-webclient-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-tests-http2 diff --git a/webclient/tests/pom.xml b/webclient/tests/pom.xml index a8febb0986d..31fa199711e 100644 --- a/webclient/tests/pom.xml +++ b/webclient/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webclient.tests diff --git a/webclient/tests/webclient/pom.xml b/webclient/tests/webclient/pom.xml index 0c51699e068..9eb0e73d633 100644 --- a/webclient/tests/webclient/pom.xml +++ b/webclient/tests/webclient/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient.tests helidon-webclient-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-tests-webclient diff --git a/webclient/tracing/pom.xml b/webclient/tracing/pom.xml index d71049e6ed8..98712d72b4d 100644 --- a/webclient/tracing/pom.xml +++ b/webclient/tracing/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-tracing diff --git a/webclient/webclient/pom.xml b/webclient/webclient/pom.xml index cfbf3f955fa..8c7bdc65f89 100644 --- a/webclient/webclient/pom.xml +++ b/webclient/webclient/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient diff --git a/webclient/websocket/pom.xml b/webclient/websocket/pom.xml index 08dfc77778e..1accfc0655f 100644 --- a/webclient/websocket/pom.xml +++ b/webclient/websocket/pom.xml @@ -21,7 +21,7 @@ io.helidon.webclient helidon-webclient-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webclient-websocket diff --git a/webserver/access-log/pom.xml b/webserver/access-log/pom.xml index 1e6ce1f5699..af6e8a4bd99 100644 --- a/webserver/access-log/pom.xml +++ b/webserver/access-log/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-access-log diff --git a/webserver/context/pom.xml b/webserver/context/pom.xml index 1af266f3640..5f0933cb04b 100644 --- a/webserver/context/pom.xml +++ b/webserver/context/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-context diff --git a/webserver/cors/pom.xml b/webserver/cors/pom.xml index 51cee0e4671..c957ebb3edd 100644 --- a/webserver/cors/pom.xml +++ b/webserver/cors/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-cors diff --git a/webserver/graphql/pom.xml b/webserver/graphql/pom.xml index 6bdddf345a4..cb38805b571 100644 --- a/webserver/graphql/pom.xml +++ b/webserver/graphql/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/webserver/grpc/pom.xml b/webserver/grpc/pom.xml index caa0014e6c0..b20c32c4cd7 100644 --- a/webserver/grpc/pom.xml +++ b/webserver/grpc/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-grpc diff --git a/webserver/http2/pom.xml b/webserver/http2/pom.xml index 8dc1227b453..54aeef34fd1 100644 --- a/webserver/http2/pom.xml +++ b/webserver/http2/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-http2 diff --git a/webserver/observe/config/pom.xml b/webserver/observe/config/pom.xml index 1a4d96b997c..72bd781c1c4 100644 --- a/webserver/observe/config/pom.xml +++ b/webserver/observe/config/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-config diff --git a/webserver/observe/health/pom.xml b/webserver/observe/health/pom.xml index 97bba4619d7..6fd004a2244 100644 --- a/webserver/observe/health/pom.xml +++ b/webserver/observe/health/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-health diff --git a/webserver/observe/info/pom.xml b/webserver/observe/info/pom.xml index 016b9ba19ab..43a06623744 100644 --- a/webserver/observe/info/pom.xml +++ b/webserver/observe/info/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-info diff --git a/webserver/observe/log/pom.xml b/webserver/observe/log/pom.xml index e05c1a64434..fe7ec26074c 100644 --- a/webserver/observe/log/pom.xml +++ b/webserver/observe/log/pom.xml @@ -22,7 +22,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-log diff --git a/webserver/observe/metrics/pom.xml b/webserver/observe/metrics/pom.xml index 6f8e9c31947..9f1422511e0 100644 --- a/webserver/observe/metrics/pom.xml +++ b/webserver/observe/metrics/pom.xml @@ -22,7 +22,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-metrics diff --git a/webserver/observe/observe/pom.xml b/webserver/observe/observe/pom.xml index 373d44634ef..e03c1d1398b 100644 --- a/webserver/observe/observe/pom.xml +++ b/webserver/observe/observe/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe diff --git a/webserver/observe/pom.xml b/webserver/observe/pom.xml index 62a61d39192..f1e083b925f 100644 --- a/webserver/observe/pom.xml +++ b/webserver/observe/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webserver.observe diff --git a/webserver/observe/tracing/pom.xml b/webserver/observe/tracing/pom.xml index 9fcdd26bc91..355ea4a973e 100644 --- a/webserver/observe/tracing/pom.xml +++ b/webserver/observe/tracing/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.observe helidon-webserver-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-tracing diff --git a/webserver/pom.xml b/webserver/pom.xml index ffaad454ecd..8e2c0122fda 100644 --- a/webserver/pom.xml +++ b/webserver/pom.xml @@ -24,7 +24,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webserver helidon-webserver-project diff --git a/webserver/security/pom.xml b/webserver/security/pom.xml index 47591c12bff..8f7b34cbb62 100644 --- a/webserver/security/pom.xml +++ b/webserver/security/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-security diff --git a/webserver/service-common/pom.xml b/webserver/service-common/pom.xml index 0da6fbc6104..117f2c48dd9 100644 --- a/webserver/service-common/pom.xml +++ b/webserver/service-common/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-service-common diff --git a/webserver/sse/pom.xml b/webserver/sse/pom.xml index 35b079f05c0..31c610df48e 100644 --- a/webserver/sse/pom.xml +++ b/webserver/sse/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-sse diff --git a/webserver/static-content/pom.xml b/webserver/static-content/pom.xml index 7491dab922e..e6b7c3d647a 100644 --- a/webserver/static-content/pom.xml +++ b/webserver/static-content/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-static-content diff --git a/webserver/testing/junit5/http2/pom.xml b/webserver/testing/junit5/http2/pom.xml index a337ed81429..af23ead0463 100644 --- a/webserver/testing/junit5/http2/pom.xml +++ b/webserver/testing/junit5/http2/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver.testing.junit5 helidon-webserver-testing-junit5-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-testing-junit5-http2 diff --git a/webserver/testing/junit5/junit5/pom.xml b/webserver/testing/junit5/junit5/pom.xml index 0222a08cf0f..53f4b20319d 100644 --- a/webserver/testing/junit5/junit5/pom.xml +++ b/webserver/testing/junit5/junit5/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver.testing.junit5 helidon-webserver-testing-junit5-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-testing-junit5 Helidon WebServer Testing JUnit5 diff --git a/webserver/testing/junit5/pom.xml b/webserver/testing/junit5/pom.xml index 0994ebe57cb..cbd079a5982 100644 --- a/webserver/testing/junit5/pom.xml +++ b/webserver/testing/junit5/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver.testing helidon-webserver-testing-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webserver.testing.junit5 diff --git a/webserver/testing/junit5/websocket/pom.xml b/webserver/testing/junit5/websocket/pom.xml index 854c3ca952d..83faec38b9b 100644 --- a/webserver/testing/junit5/websocket/pom.xml +++ b/webserver/testing/junit5/websocket/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver.testing.junit5 helidon-webserver-testing-junit5-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-testing-junit5-websocket diff --git a/webserver/testing/pom.xml b/webserver/testing/pom.xml index 65cec7aa753..61e89272220 100644 --- a/webserver/testing/pom.xml +++ b/webserver/testing/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webserver.testing diff --git a/webserver/tests/access-log/pom.xml b/webserver/tests/access-log/pom.xml index d9fa51d0b6d..f41d471b749 100644 --- a/webserver/tests/access-log/pom.xml +++ b/webserver/tests/access-log/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-access-log diff --git a/webserver/tests/gh2631/pom.xml b/webserver/tests/gh2631/pom.xml index 8e19542698d..02e32164d63 100644 --- a/webserver/tests/gh2631/pom.xml +++ b/webserver/tests/gh2631/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-gh2631 diff --git a/webserver/tests/grpc/pom.xml b/webserver/tests/grpc/pom.xml index 6d98c43130c..5dde2e4d8a6 100644 --- a/webserver/tests/grpc/pom.xml +++ b/webserver/tests/grpc/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-grpc diff --git a/webserver/tests/http2/pom.xml b/webserver/tests/http2/pom.xml index fccc3cd88e5..b26acc12dbf 100644 --- a/webserver/tests/http2/pom.xml +++ b/webserver/tests/http2/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-http2 diff --git a/webserver/tests/imperative/pom.xml b/webserver/tests/imperative/pom.xml index 30da94eb2e0..be6c65fa4fb 100644 --- a/webserver/tests/imperative/pom.xml +++ b/webserver/tests/imperative/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-imperative diff --git a/webserver/tests/mtls/pom.xml b/webserver/tests/mtls/pom.xml index 36cd959afd3..908c151203e 100644 --- a/webserver/tests/mtls/pom.xml +++ b/webserver/tests/mtls/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-mtls diff --git a/webserver/tests/observe/health/pom.xml b/webserver/tests/observe/health/pom.xml index 5aa55b60f90..46d2754611f 100644 --- a/webserver/tests/observe/health/pom.xml +++ b/webserver/tests/observe/health/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests.observe helidon-webserver-tests-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-tests-health diff --git a/webserver/tests/observe/observe/pom.xml b/webserver/tests/observe/observe/pom.xml index 2c0b005c151..4ed80f4fa85 100644 --- a/webserver/tests/observe/observe/pom.xml +++ b/webserver/tests/observe/observe/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests.observe helidon-webserver-tests-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-tests-observe diff --git a/webserver/tests/observe/pom.xml b/webserver/tests/observe/pom.xml index ddc5f7fc7b3..a83bd76e2e2 100644 --- a/webserver/tests/observe/pom.xml +++ b/webserver/tests/observe/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webserver.tests.observe diff --git a/webserver/tests/observe/security/pom.xml b/webserver/tests/observe/security/pom.xml index d058731baa8..6191ba53f6c 100644 --- a/webserver/tests/observe/security/pom.xml +++ b/webserver/tests/observe/security/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests.observe helidon-webserver-tests-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-observe-tests-security diff --git a/webserver/tests/observe/weighted/pom.xml b/webserver/tests/observe/weighted/pom.xml index eed5df607a0..5a638191adb 100644 --- a/webserver/tests/observe/weighted/pom.xml +++ b/webserver/tests/observe/weighted/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests.observe helidon-webserver-tests-observe-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-observe-weighted diff --git a/webserver/tests/pom.xml b/webserver/tests/pom.xml index 430d5920cc3..926bdd70082 100644 --- a/webserver/tests/pom.xml +++ b/webserver/tests/pom.xml @@ -24,7 +24,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.webserver.tests diff --git a/webserver/tests/resource-limits/pom.xml b/webserver/tests/resource-limits/pom.xml index 4e673b53173..e30549708be 100644 --- a/webserver/tests/resource-limits/pom.xml +++ b/webserver/tests/resource-limits/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-resource-limits diff --git a/webserver/tests/sse/pom.xml b/webserver/tests/sse/pom.xml index 68d6d0e5839..118c80fb763 100644 --- a/webserver/tests/sse/pom.xml +++ b/webserver/tests/sse/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-sse diff --git a/webserver/tests/static-content/pom.xml b/webserver/tests/static-content/pom.xml index 1e0e8903192..60e0a4e468f 100644 --- a/webserver/tests/static-content/pom.xml +++ b/webserver/tests/static-content/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-tests-static-content diff --git a/webserver/tests/upgrade/pom.xml b/webserver/tests/upgrade/pom.xml index fdd955d4048..b3e91a036c2 100644 --- a/webserver/tests/upgrade/pom.xml +++ b/webserver/tests/upgrade/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-upgrade diff --git a/webserver/tests/webserver/pom.xml b/webserver/tests/webserver/pom.xml index 448d33930f3..20d7d142f0f 100644 --- a/webserver/tests/webserver/pom.xml +++ b/webserver/tests/webserver/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-weserver-tests-webserver diff --git a/webserver/tests/websocket/pom.xml b/webserver/tests/websocket/pom.xml index 55da871d709..8ae2b0e0a1c 100644 --- a/webserver/tests/websocket/pom.xml +++ b/webserver/tests/websocket/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver.tests helidon-webserver-tests-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-tests-websocket diff --git a/webserver/webserver/pom.xml b/webserver/webserver/pom.xml index f9915e2315a..ce3ca761a85 100644 --- a/webserver/webserver/pom.xml +++ b/webserver/webserver/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver diff --git a/webserver/websocket/pom.xml b/webserver/websocket/pom.xml index 7003901f00a..1b9b7fe1603 100644 --- a/webserver/websocket/pom.xml +++ b/webserver/websocket/pom.xml @@ -21,7 +21,7 @@ io.helidon.webserver helidon-webserver-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT helidon-webserver-websocket diff --git a/websocket/pom.xml b/websocket/pom.xml index 81f645ae5d2..cd988f69e90 100644 --- a/websocket/pom.xml +++ b/websocket/pom.xml @@ -21,7 +21,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.websocket From b68b611cd24c873933a39403de82e7594968b4fa Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Thu, 30 May 2024 12:16:35 -0700 Subject: [PATCH 033/245] 4.x: Repair version strings (#8820) * Repair version strings * Fix copyright --- examples/webserver/threads/pom.xml | 4 ++-- grpc/core/pom.xml | 2 +- grpc/pom.xml | 2 +- tests/integration/dbclient/mongodb/pom.xml | 4 ++-- webclient/grpc/pom.xml | 2 +- webclient/tests/grpc/pom.xml | 2 +- webserver/testing/junit5/grpc/pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/webserver/threads/pom.xml b/examples/webserver/threads/pom.xml index 3fc7bbf87e5..a201c6abe85 100644 --- a/examples/webserver/threads/pom.xml +++ b/examples/webserver/threads/pom.xml @@ -23,12 +23,12 @@ io.helidon.applications helidon-se - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT ../../../applications/se/pom.xml io.helidon.examples.webserver helidon-examples-webserver-threads - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT io.helidon.examples.webserver.threads.Main diff --git a/grpc/core/pom.xml b/grpc/core/pom.xml index a1a059712c2..ab16615f330 100644 --- a/grpc/core/pom.xml +++ b/grpc/core/pom.xml @@ -21,7 +21,7 @@ helidon-grpc-project io.helidon.grpc - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT 4.0.0 diff --git a/grpc/pom.xml b/grpc/pom.xml index 2bbfdce4d6e..be4cddcc811 100644 --- a/grpc/pom.xml +++ b/grpc/pom.xml @@ -22,7 +22,7 @@ io.helidon helidon-project - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom diff --git a/tests/integration/dbclient/mongodb/pom.xml b/tests/integration/dbclient/mongodb/pom.xml index ef4a71c4596..9ece9d7fe3b 100644 --- a/tests/integration/dbclient/mongodb/pom.xml +++ b/tests/integration/dbclient/mongodb/pom.xml @@ -1,6 +1,6 @@ @@ -240,19 +240,12 @@ managed] [source,xml] ---- - com.oracle.database.jdbc - ojdbc11 - runtime + io.helidon.integrations.db + ojdbc + runtime ---- -<1> See - https://www.oracle.com/database/technologies/maven-central-guide.html[Developer's - Guide For Oracle JDBC 21c on Maven Central] for more details. -<2> The `ojdbc11` artifact - https://www.oracle.com/database/technologies/maven-central-guide.html#:~:text=ojdbc11.jar,JDK14%20and%20JDK15[implements - relevant parts of the JDBC 4.3 specification], which forms part of - Java 21, which is the Java version targeted by Helidon 4. -<3> The `scope` is `runtime`, indicating that the Oracle +<1> The `scope` is `runtime`, indicating that the Oracle JDBC driver classes will be available on the runtime classpath. === Configuration [[DS-Configuration]] diff --git a/examples/employee-app/pom.xml b/examples/employee-app/pom.xml index 4a109a3a49e..29009f7c42b 100644 --- a/examples/employee-app/pom.xml +++ b/examples/employee-app/pom.xml @@ -37,10 +37,13 @@ + + io.helidon.integrations.db + ojdbc + com.oracle.database.jdbc - ojdbc11-production - pom + ucp io.helidon.webserver diff --git a/examples/integrations/cdi/datasource-hikaricp/pom.xml b/examples/integrations/cdi/datasource-hikaricp/pom.xml index 8f90dfada21..7c4171d7cd0 100644 --- a/examples/integrations/cdi/datasource-hikaricp/pom.xml +++ b/examples/integrations/cdi/datasource-hikaricp/pom.xml @@ -51,9 +51,8 @@ - com.oracle.database.jdbc - ojdbc11-production - pom + io.helidon.integrations.db + ojdbc runtime diff --git a/examples/integrations/micronaut/data/README.md b/examples/integrations/micronaut/data/README.md index f08ff99967f..8943e6f49e9 100644 --- a/examples/integrations/micronaut/data/README.md +++ b/examples/integrations/micronaut/data/README.md @@ -61,10 +61,14 @@ curl -i http://localhost:8080/pets/s - Update ./pom.xml to replace dependency on com.h2database with following ``` + + io.helidon.integrations.db + ojdbc + runtime + com.oracle.database.jdbc - ojdbc8-production - pom + ucp runtime ``` @@ -102,10 +106,14 @@ curl -i http://localhost:8080/pets/s - Update ./pom.xml to replace dependency on com.h2database with following ``` + + io.helidon.integrations.db + ojdbc + runtime + com.oracle.database.jdbc - ojdbc8-production - pom + ucp runtime ``` diff --git a/examples/integrations/oci/atp-cdi/pom.xml b/examples/integrations/oci/atp-cdi/pom.xml index f5245d6beb9..484b2ad1778 100644 --- a/examples/integrations/oci/atp-cdi/pom.xml +++ b/examples/integrations/oci/atp-cdi/pom.xml @@ -42,10 +42,13 @@ io.helidon.integrations.cdi helidon-integrations-cdi-datasource-ucp + + io.helidon.integrations.db + ojdbc + com.oracle.database.jdbc - ojdbc11-production - pom + ucp io.helidon.integrations.oci.sdk diff --git a/examples/integrations/oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties b/examples/integrations/oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties deleted file mode 100644 index 2ef2caf1d93..00000000000 --- a/examples/integrations/oci/atp-cdi/src/main/resources/META-INF/helidon/serial-config.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2022 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. -# - -pattern=oracle.sql.converter.* diff --git a/examples/integrations/oci/atp/pom.xml b/examples/integrations/oci/atp/pom.xml index 5e1f3985d76..c62df627331 100644 --- a/examples/integrations/oci/atp/pom.xml +++ b/examples/integrations/oci/atp/pom.xml @@ -50,10 +50,13 @@ io.helidon.dbclient helidon-dbclient-jdbc + + io.helidon.integrations.db + ojdbc + com.oracle.database.jdbc - ojdbc11-production - pom + ucp io.helidon.config diff --git a/integrations/cdi/datasource-ucp/pom.xml b/integrations/cdi/datasource-ucp/pom.xml index 1b7c097ee77..21d36266b65 100644 --- a/integrations/cdi/datasource-ucp/pom.xml +++ b/integrations/cdi/datasource-ucp/pom.xml @@ -40,10 +40,14 @@ helidon-integrations-cdi-datasource compile + + io.helidon.integrations.db + ojdbc + compile + com.oracle.database.jdbc - ojdbc11-production - pom + ucp compile diff --git a/integrations/db/ojdbc/src/main/resources/META-INF/helidon/serial-config.properties b/integrations/db/ojdbc/src/main/resources/META-INF/helidon/serial-config.properties index 85f9e5a6de0..b91bd24519f 100644 --- a/integrations/db/ojdbc/src/main/resources/META-INF/helidon/serial-config.properties +++ b/integrations/db/ojdbc/src/main/resources/META-INF/helidon/serial-config.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. @@ -14,4 +14,4 @@ # limitations under the License. # -pattern=oracle.sql.converter.* +pattern=oracle.i18n.util.builder.*;oracle.sql.converter.*;oracle.i18n.text.*;oracle.i18n.text.converter.* diff --git a/messaging/connectors/aq/pom.xml b/messaging/connectors/aq/pom.xml index 9b294994814..0b7a51a630e 100644 --- a/messaging/connectors/aq/pom.xml +++ b/messaging/connectors/aq/pom.xml @@ -61,10 +61,13 @@ com.oracle.database.messaging aqapi + + io.helidon.integrations.db + ojdbc + com.oracle.database.jdbc - ojdbc11-production - pom + ucp jakarta.transaction From ea90af920527359e97d9358cfeb484ff4fc84790 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 3 Jun 2024 10:24:04 -0500 Subject: [PATCH 036/245] Retrieve the correct requested URI info path value, indpt of the routing path used to locate the handler (#8823) --- .../webserver/http2/Http2ServerRequest.java | 2 +- webserver/tests/pom.xml | 1 + .../tests/requested-uri-path-gh8818/pom.xml | 60 +++++++++++++++++++ .../webserver/tests/gh2631/Gh8818.java | 60 +++++++++++++++++++ .../src/main/resources/logging.properties | 20 +++++++ .../webserver/tests/gh2631/Gh8818Test.java | 54 +++++++++++++++++ .../webserver/http1/Http1ServerRequest.java | 2 +- 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 webserver/tests/requested-uri-path-gh8818/pom.xml create mode 100644 webserver/tests/requested-uri-path-gh8818/src/main/java/io/helidon/webserver/tests/gh2631/Gh8818.java create mode 100644 webserver/tests/requested-uri-path-gh8818/src/main/resources/logging.properties create mode 100644 webserver/tests/requested-uri-path-gh8818/src/test/java/io/helidon/webserver/tests/gh2631/Gh8818Test.java diff --git a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ServerRequest.java b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ServerRequest.java index 42185e185ee..34ba67d04a0 100644 --- a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ServerRequest.java +++ b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ServerRequest.java @@ -238,7 +238,7 @@ private UriInfo createUriInfo() { .orElse(DEFAULT_REQUESTED_URI_DISCOVERY_CONTEXT) .uriInfo(remotePeer().address().toString(), localPeer().address().toString(), - path.path(), + path.absolute().path(), headers, query(), isSecure()); diff --git a/webserver/tests/pom.xml b/webserver/tests/pom.xml index 926bdd70082..21e3340e0a0 100644 --- a/webserver/tests/pom.xml +++ b/webserver/tests/pom.xml @@ -41,6 +41,7 @@ imperative mtls observe + requested-uri-path-gh8818 resource-limits sse static-content diff --git a/webserver/tests/requested-uri-path-gh8818/pom.xml b/webserver/tests/requested-uri-path-gh8818/pom.xml new file mode 100644 index 00000000000..426ac262ffb --- /dev/null +++ b/webserver/tests/requested-uri-path-gh8818/pom.xml @@ -0,0 +1,60 @@ + + + + 4.0.0 + + io.helidon.webserver.tests + helidon-webserver-tests-project + 4.1.0-SNAPSHOT + + + helidon-webserver-tests-requested-uri-path-gh8818 + Helidon WebServer Tests Requested URI Path GH 8818 + + + + io.helidon.webserver + helidon-webserver + + + io.helidon.webserver + helidon-webserver-static-content + + + io.helidon.webserver.testing.junit5 + helidon-webserver-testing-junit5 + test + + + io.helidon.logging + helidon-logging-jul + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + + \ No newline at end of file diff --git a/webserver/tests/requested-uri-path-gh8818/src/main/java/io/helidon/webserver/tests/gh2631/Gh8818.java b/webserver/tests/requested-uri-path-gh8818/src/main/java/io/helidon/webserver/tests/gh2631/Gh8818.java new file mode 100644 index 00000000000..fd84e7bb43d --- /dev/null +++ b/webserver/tests/requested-uri-path-gh8818/src/main/java/io/helidon/webserver/tests/gh2631/Gh8818.java @@ -0,0 +1,60 @@ +/* + * 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.webserver.tests.gh2631; + +import io.helidon.logging.common.LogConfig; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.http.HttpRouting; +import io.helidon.webserver.http.HttpRules; +import io.helidon.webserver.http.HttpService; +import io.helidon.webserver.http.ServerRequest; +import io.helidon.webserver.http.ServerResponse; + +public class Gh8818 { + + static final String ENDPOINT_PATH = "/greet"; + + public static void main(String[] args) { + startServer(); + } + + static WebServer startServer() { + return WebServer.builder() + .routing(Gh8818::routing) + .build() + .start(); + } + + static void routing(HttpRouting.Builder routing) { + LogConfig.configureRuntime(); + + routing.register(ENDPOINT_PATH, new TestResource()); + } + + private static class TestResource implements HttpService { + + @Override + public void routing(HttpRules httpRules) { + httpRules.get("/", this::getDefaultMessageHandler); + } + + private void getDefaultMessageHandler(ServerRequest serverRequest, + ServerResponse serverResponse) { + serverResponse.send(serverRequest.requestedUri().path().path()); + } + } +} diff --git a/webserver/tests/requested-uri-path-gh8818/src/main/resources/logging.properties b/webserver/tests/requested-uri-path-gh8818/src/main/resources/logging.properties new file mode 100644 index 00000000000..69838509624 --- /dev/null +++ b/webserver/tests/requested-uri-path-gh8818/src/main/resources/logging.properties @@ -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. +# + +handlers=io.helidon.logging.jul.HelidonConsoleHandler +java.util.logging.SimpleFormatter.format=%4$s %3$s: %5$s%6$s%n + +.level=INFO diff --git a/webserver/tests/requested-uri-path-gh8818/src/test/java/io/helidon/webserver/tests/gh2631/Gh8818Test.java b/webserver/tests/requested-uri-path-gh8818/src/test/java/io/helidon/webserver/tests/gh2631/Gh8818Test.java new file mode 100644 index 00000000000..9ba34ce62f2 --- /dev/null +++ b/webserver/tests/requested-uri-path-gh8818/src/test/java/io/helidon/webserver/tests/gh2631/Gh8818Test.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.webserver.tests.gh2631; + +import io.helidon.webclient.http1.Http1Client; +import io.helidon.webserver.http.HttpRouting; +import io.helidon.webserver.testing.junit5.ServerTest; +import io.helidon.webserver.testing.junit5.SetUpRoute; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@ServerTest +class Gh8818Test { + + private Http1Client client; + + Gh8818Test(Http1Client client) { + this.client = client; + } + + @SetUpRoute + static void setupRoute(HttpRouting.Builder routing) { + Gh8818.routing(routing); + } + + @Test + void checkForFullPath() { + String requestedPath = get(Gh8818.ENDPOINT_PATH); + assertThat("Requested path", requestedPath, is(Gh8818.ENDPOINT_PATH)); + } + + private String get(String path) { + return client.get() + .path(path) + .requestEntity(String.class); + } +} \ No newline at end of file diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java index 78d5e4a92ef..84a7a1e5e9c 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java @@ -225,7 +225,7 @@ private UriInfo createUriInfo() { .orElse(DEFAULT_REQUESTED_URI_DISCOVERY_CONTEXT) .uriInfo(remotePeer().address().toString(), localPeer().address().toString(), - path.path(), + path.absolute().path(), headers, query(), isSecure()); From 9ce57d63918ad00508b06a47faed7ee53a4e6310 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Mon, 3 Jun 2024 22:49:10 +0200 Subject: [PATCH 037/245] Fix handling of invalid end of line in HTTP header parsing. Added tests (#8831) --- .../testing/http/junit5/SocketHttpClient.java | 19 ++++++++++ .../webserver/tests/BadRequestTest.java | 35 ++++++++++++++++++- .../helidon/webserver/http1/Http1Headers.java | 4 +-- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/SocketHttpClient.java b/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/SocketHttpClient.java index b89f676d843..92ec6aed91a 100644 --- a/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/SocketHttpClient.java +++ b/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/SocketHttpClient.java @@ -460,6 +460,25 @@ public void request(String method, String path, String protocol, String host, It } } + /** + * Send raw data to the server. + * + * @param content content to send over the socket + */ + public void requestRaw(String content) { + if (socket == null) { + connect(); + } + + try { + PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)); + pw.print(content); + pw.flush(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + /** * Write raw proxy protocol header before a request. * diff --git a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadRequestTest.java b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadRequestTest.java index e1e0d9eb080..59237c6183d 100644 --- a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadRequestTest.java +++ b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadRequestTest.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. @@ -148,6 +148,39 @@ void testBadHeaderWhitespace() { assertThat(response, containsString(CUSTOM_ENTITY)); } + @Test + void testCrWithoutLf() { + socketClient.requestRaw("GET / HTTP/1.1\r\nhost: localhost:8080\rcustom: value\r\n"); + + String response = socketClient.receive(); + assertThat(response, containsString("400 " + CUSTOM_REASON_PHRASE)); + assertThat(response, containsString(CUSTOM_ENTITY)); + } + + @Test + void testLfWithoutCr() { + socketClient.requestRaw("GET / HTTP/1.1\r\nhost: localhost:8080\ncustom: value\r\n"); + + String response = socketClient.receive(); + assertThat(response, containsString("400 " + CUSTOM_REASON_PHRASE)); + assertThat(response, containsString(CUSTOM_ENTITY)); + } + + @Test + void testKeepAliveAndMissingLf() { + socketClient.request(Method.GET, "/", null, List.of("Accept: text/plain", "Connection: keep-alive")); + String response = socketClient.receive(); + assertThat(response, containsString("200 OK")); + assertThat(response, containsString("Hi")); + + socketClient.requestRaw("GET / HTTP/1.1\r\nhost: localhost:8080\rcustom: value\r\n"); + + response = socketClient.receive(); + assertThat(response, containsString("400 " + CUSTOM_REASON_PHRASE)); + assertThat(response, containsString("Connection: close")); + assertThat(response, containsString(CUSTOM_ENTITY)); + } + private static DirectHandler.TransportResponse badRequestHandler(DirectHandler.TransportRequest request, DirectHandler.EventType eventType, Status httpStatus, diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Headers.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Headers.java index 1934a4fd672..0255b3fcf4c 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Headers.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Headers.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. @@ -55,7 +55,7 @@ public Http1Headers(DataReader reader, int maxHeadersSize, boolean validateHeade public WritableHeaders readHeaders(HttpPrologue prologue) { try { return Http1HeadersParser.readHeaders(reader, maxHeadersSize, validateHeaders); - } catch (IllegalStateException | IllegalArgumentException e) { + } catch (IllegalStateException | IllegalArgumentException | DataReader.IncorrectNewLineException e) { throw RequestException.builder() .type(DirectHandler.EventType.BAD_REQUEST) .request(DirectTransportRequest.create(prologue, WritableHeaders.create())) From 0b76c6c8291d42a086ac8da044cf7d84fd82e0fb Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Tue, 4 Jun 2024 06:44:16 +0200 Subject: [PATCH 038/245] Doc for @AddConfigBlock #8807 (#8825) Doc for @AddConfigBlock #8807 Signed-off-by: Jorge Bescos Gascon --- .../asciidoc/mp/guides/testing-junit5.adoc | 6 ++++- docs/src/main/asciidoc/mp/testing-ng.adoc | 2 +- docs/src/main/asciidoc/mp/testing.adoc | 2 +- .../docs/mp/guides/TestingJunit5Snippets.java | 23 +++++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc b/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc index 1a3bec3b795..4a527b50bc6 100644 --- a/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc +++ b/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc @@ -103,7 +103,11 @@ The testing extension supports a few additional annotations that allow for finer | `@HelidonTest(resetPerTest = true)` | Resets the container for each method. This is useful when we want to modify configuration or beans between executions. In such a case, injection into fields is not possible, as we would need a different instance for each test. -| `@AddConfig(key = "app.greeting", value = "Unite")` | Defines a new configuration (either on class level, or method level) by adding a single configuration key/value. +| `@AddConfig(key = "app.greeting", value = "Unite")` | Define additional configuration (either on class level, or method level) by adding a single configuration key/value. +| `@AddConfigBlock(type = "properties", value = """ + + some.key1=some.value1 + + some.key2=some.value2 + +""")` | Define additional configuration (either on class level, or method level) by adding one or more configuration key/value pairs. | `@Configuration(configSources = "test-config.properties")` | Adds a whole config source from classpath. |==================== diff --git a/docs/src/main/asciidoc/mp/testing-ng.adoc b/docs/src/main/asciidoc/mp/testing-ng.adoc index 55299c13cca..a4f10e31ec4 100644 --- a/docs/src/main/asciidoc/mp/testing-ng.adoc +++ b/docs/src/main/asciidoc/mp/testing-ng.adoc @@ -71,7 +71,7 @@ This will change the behavior as follows: - you cannot inject fields or constructor parameters of the test class itself (as a single instance is shared by more containers) === Usage - configuration -In addition to the `@AddConfig` annotation, you can also use +In addition to the `@AddConfig` and `@AddConfigBlock` annotations, you can also use `@Configuration` to configure additional classpath properties config sources using `configSources`, and to mark that a custom configuration is desired. If `@Configuration(useExisting=true)`, the existing (or default) MicroProfile configuration would be used. In this case diff --git a/docs/src/main/asciidoc/mp/testing.adoc b/docs/src/main/asciidoc/mp/testing.adoc index c61c63e68a0..28ff6c10a43 100644 --- a/docs/src/main/asciidoc/mp/testing.adoc +++ b/docs/src/main/asciidoc/mp/testing.adoc @@ -67,7 +67,7 @@ This will change the behavior as follows: - you can add `SeContainer` as a method parameter of any test method and you will get the current container === Usage - configuration -In addition to the `@AddConfig` annotation, you can also use +In addition to the `@AddConfig` and `@AddConfigBlock` annotations, you can also use `@Configuration` to configure additional classpath properties config sources using `configSources`, and to mark that a custom configuration is desired. If `@Configuration(useExisting=true)`, the existing (or default) MicroProfile configuration would be used. In this case diff --git a/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java b/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java index 155f53cbe0e..03b7fcd1ea3 100644 --- a/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/guides/TestingJunit5Snippets.java @@ -18,6 +18,7 @@ import io.helidon.microprofile.config.ConfigCdiExtension; import io.helidon.microprofile.testing.junit5.AddBean; import io.helidon.microprofile.testing.junit5.AddConfig; +import io.helidon.microprofile.testing.junit5.AddConfigBlock; import io.helidon.microprofile.testing.junit5.AddExtension; import io.helidon.microprofile.testing.junit5.DisableDiscovery; import io.helidon.microprofile.testing.junit5.HelidonTest; @@ -104,6 +105,28 @@ private void validate(WebTarget webTarget, assertThat(message, is("Message in JSON")); } } + + @HelidonTest + @AddConfigBlock(""" + some.key1=some.value1 + some.key2=some.value2 + """) + class AddConfigBlockTest { + + @Inject + @ConfigProperty(name = "some.key1") + private String value1; + + @Inject + @ConfigProperty(name = "some.key2") + private String value2; + + @Test + void testValue() { + assertThat(value1, is("some.value1")); + assertThat(value2, is("some.value2")); + } + } // end::snippet_3[] } From 1977286b492200fcf6ed1000d3b79cb2010a250a Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 4 Jun 2024 11:02:19 -0400 Subject: [PATCH 039/245] Restored test TenantTest#test2 after implementing a new method caching strategy in fault tolerance. (#8832) --- .../faulttolerance/MethodInvoker.java | 41 +++++++++++++++++-- .../functional/requestscope/TenantTest.java | 2 - 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java b/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java index 1c91b24f083..10d90de2575 100644 --- a/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java +++ b/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.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. @@ -18,15 +18,17 @@ import java.lang.reflect.Method; import java.time.Duration; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; import java.util.function.Supplier; import io.helidon.common.context.Context; @@ -86,7 +88,8 @@ class MethodInvoker implements FtSupplier { * caches the FT handler as well as some additional variables. This mapping must * be shared by all instances of this class. */ - private static final ConcurrentHashMap METHOD_STATES = new ConcurrentHashMap<>(); + private static final MethodStateCache METHOD_STATES = new MethodStateCache(); + /** * The method being intercepted. */ @@ -693,7 +696,7 @@ private static class MethodState { /** * A key used to lookup {@code MethodState} instances, which include FT handlers. * A class loader is necessary to support multiple applications as seen in the TCKs. - * The method class in necessary given that the same method can inherited by different + * The method class in necessary given that the same method can inherit by different * classes with different FT annotations and should not share handlers. Finally, the * method is main part of the key. */ @@ -727,4 +730,34 @@ public int hashCode() { return Objects.hash(classLoader, methodClass, method); } } + + /** + * Used instead of a {@link java.util.concurrent.ConcurrentHashMap} to avoid some + * locking problems. + */ + private static class MethodStateCache { + + private final ReentrantLock lock = new ReentrantLock(); + private final Map cache = new HashMap<>(); + + MethodState computeIfAbsent(MethodStateKey key, Function function) { + lock.lock(); + try { + MethodState methodState = cache.get(key); + if (methodState != null) { + return methodState; + } + MethodState newMethodState = function.apply(key); + Objects.requireNonNull(newMethodState); + cache.put(key, newMethodState); + return newMethodState; + } finally { + lock.unlock(); + } + } + + void clear() { + cache.clear(); + } + } } diff --git a/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java b/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java index b9311132912..97a02ceef94 100644 --- a/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java +++ b/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java @@ -26,7 +26,6 @@ import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -47,7 +46,6 @@ public void test() throws Exception { } @Test - @Disabled // issue #8813 public void test2() throws Exception { asyncCalls(() -> baseTarget.path("test2").request() .header("x-tenant-id", "123").get(), null); From 2ff288b20e659c5963304359c26c3b3df4e700b4 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Tue, 4 Jun 2024 20:42:04 +0200 Subject: [PATCH 040/245] 4.x: register routing in weighted order of Server and HTTP Features (#8826) * Correctly handle order of Server Features and HTTP Features when registering non-HTTP Feature elements to the routing. With the updated version, all elements (filters, routes, services) are ordered depending on the feature's weight. This is achieved by creating a new HttpFeature for each Server Feature with the same weight, that collects all registrations and applies them once the HTTP Features are ordered by weight. HttpFeature registered from a Server Feature is left intact and applied on the real builder (as this already works as it should) * Updated CORS weight to 850 to align with the documented (and intended) ordering * Added documentation for server features. * Added documentation for Context feature. * Reorganized webserver.adoc, as some sections were within wrong chapters; updated TOC * Added `HttpFeature` documentation. --------- Co-authored-by: Daniel Kec --- docs-internal/http-features.md | 2 +- docs/src/main/asciidoc/se/webserver.adoc | 344 +++++++++++------- .../helidon/webserver/cors/CorsFeature.java | 2 +- .../webserver/tests/FeaturesOrderingTest.java | 297 +++++++++++++++ .../java/io/helidon/webserver/LoomServer.java | 10 +- .../webserver/ServerFeatureContextImpl.java | 81 ++++- .../webserver/ServerToHttpFeatureBuilder.java | 128 +++++++ .../webserver/http/HttpRoutingFeature.java | 52 +-- .../helidon/webserver/http/Registration.java | 106 ++++++ .../helidon/webserver/http/Registrations.java | 115 ++++++ 10 files changed, 940 insertions(+), 197 deletions(-) create mode 100644 webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/FeaturesOrderingTest.java create mode 100644 webserver/webserver/src/main/java/io/helidon/webserver/ServerToHttpFeatureBuilder.java create mode 100644 webserver/webserver/src/main/java/io/helidon/webserver/http/Registration.java create mode 100644 webserver/webserver/src/main/java/io/helidon/webserver/http/Registrations.java diff --git a/docs-internal/http-features.md b/docs-internal/http-features.md index 09327b9c890..61c22338833 100644 --- a/docs-internal/http-features.md +++ b/docs-internal/http-features.md @@ -8,7 +8,7 @@ Features | Context | 1100 | | Access Log | 1000 | | Tracing | 900 | -| CORS | 950 | +| CORS | 850 | | Security | 800 | | Routing (all handlers) | 100 | | OpenAPI | 90 | diff --git a/docs/src/main/asciidoc/se/webserver.adoc b/docs/src/main/asciidoc/se/webserver.adoc index 42381b7dbc4..e83d9ca6490 100644 --- a/docs/src/main/asciidoc/se/webserver.adoc +++ b/docs/src/main/asciidoc/se/webserver.adoc @@ -32,10 +32,14 @@ include::{rootdir}/includes/se.adoc[] - <> ** <> ** <> +** <> ** <> *** <> *** <> *** <> +- <> +** <> +** <> - <> ** <> ** <> @@ -96,6 +100,85 @@ include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_2, indent=0] just use `Config.create()` <2> Server expects the configuration tree located on the node of `server` +=== Configuring TLS + +Configure TLS either programmatically, or by the Helidon configuration framework. + +==== Configuring TLS in Your Code + +To configure TLS in WebServer programmatically create your keystore configuration and pass it to the WebServer builder. + +[source,java] +---- +include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_30, indent=0] +---- + + +==== Configuring TLS in the Config File + +It is also possible to configure TLS via the config file. + +[source,yaml] +.WebServer TLS configuration file `application.yaml` +---- +server: + tls: + #Truststore setup + trust: + keystore: + passphrase: "password" + trust-store: true + resource: + resource-path: "keystore.p12" + # Keystore with private key and server certificate + private-key: + keystore: + passphrase: "password" + resource: + resource-path: "keystore.p12" +---- +Then, in your application code, load the configuration from that file. + +[source,java] +.WebServer initialization using the `application.yaml` file located on the classpath +---- +include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_2, indent=0] +---- +<1> `application.yaml` is a default configuration source loaded when YAML support is on classpath, so we can +just use `Config.create()` +<2> Server expects the configuration tree located on the node of `server` + +Or you can only create WebServerTls instance based on the config file. + +[source,java] +.WebServerTls instance based on `application.yaml` file located on the classpath +---- +include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_31, indent=0] +---- + +This can alternatively be configured with paths to PKCS#8 PEM files rather than KeyStores: + +[source,yaml] +.WebServer TLS configuration file `application.yaml` +---- +server: + tls: + #Truststore setup + trust: + pem: + certificates: + resource: + resource-path: "ca-bundle.pem" + private-key: + pem: + key: + resource: + resource-path: "key.pem" + cert-chain: + resource: + resource-path: "chain.pem" +---- + === Configuration Options include::{rootdir}/config/io_helidon_webserver_WebServer.adoc[leveloffset=+2,tag=config] @@ -202,6 +285,15 @@ include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_7, indent=0] ---- In this example, the `GET` handler matches requests to `/hello/subpath`. +[[anchor-http-feature]] +=== Using `HttpFeature` + +By implementing the `io.helidon.webserver.http.HttpFeature` interface, you can organize multiple routes and/or filters into +a feature, that will be setup according to its defined `io.helidon.common.Weight` (or using `io.helidon.common.Weighted`). + +Each service has access to the routing builder. HTTP Features are configured for each routing builder. If there is a need +to configure a feature for multiple sockets, you can use <> instead. + == Request Handling Implement the logic to handle requests to WebServer in a `Handler`, which is a `FunctionalInterface`. @@ -389,6 +481,126 @@ include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_21, indent=0] * Otherwise, the exceptions are translated to an Internal Server Error HTTP error code `500`. +=== Configuration Options + +include::{rootdir}/config/io_helidon_common_tls_Tls.adoc[leveloffset=+2,tag=config] + +== Server Features +Server features provide additional functionality to the WebServer, through modification of the server configuration, +listener configuration, or routing. + +A server feature can be added by implementing `io.helidon.webserver.spi.ServerFeature`. +Server features support automated discovery, as long as the implementation is available through Java `ServiceLoader`. +Server features can also be added through configuration, as can be seen above in <>, +configuration key `features`. + +All features (both `ServerFeature` and <>) honor weight of the feature +(defined either through `@Weight` annotation, or by implementing `Weighted` interface) when registering routes, +`HttpService`, or `Filter` to the routing. + +The following table shows available server features and their weight. The highest weight is always registered (and invoked) +first. + + +|=== +|Feature |Weight + +|<> +|1100 + +|<> +|1000 + +|xref:tracing.adoc[Tracing] +|900 + +|xref:cors.adoc[CORS] +|850 + +|xref:security/introduction.adoc[Security] +|800 + +|Routing (all handlers and filters) +|100 + +|xref:openapi/openapi.adoc[OpenAPI] +|90 + +|xref:observability.adoc[Observability] +|80 +|=== + +=== Context + +Context feature adds a filter that executes all requests within the context of `io.helidon.common.context.Context`. +A `Context` instance is available on `ServerRequest` even if this feature is not added. This feature adds support for +obtaining request context through `io.helidon.common.context.Contexts.context()`. + +This feature will provide the same behavior as previous versions of Helidon. Since Helidon 4.0.0, this feature is not +automatically added. + +To enable execution of routes within Context, add the following dependency to project's `pom.xml`: + +[source,xml] +---- + + io.helidon.webserver + helidon-webserver-context + +---- + +Context feature can be configured, all options shown below are also available both in config, and programmatically +when using builder. + +include::{rootdir}/config/io_helidon_webserver_context_ContextFeature.adoc[leveloffset=+1] + +=== Access Log + +Access logging in Helidon is done by a dedicated module that can be +added to WebServer and configured. + +Access logging is a Helidon WebServer `ServerFeature`. Access Log feature has a +very high weight, so it is registered before other features (such as security) that may +terminate a request. This is to ensure the log contains all requests with appropriate status codes. + +To enable Access logging add the following dependency to project's `pom.xml`: + +[source,xml] +---- + + io.helidon.webserver + helidon-webserver-access-log + +---- + +==== Configuring Access Log in Your Code + +`AccessLogFeature` is discovered automatically by default, and configured through `server.features.access-log`. +You can also configure this feature in code by registering it with WebServer (which will replace the discovered feature). + +[source,java] +---- +include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_29, indent=0] +---- + +==== Configuring Access Log in a Configuration File + +Access log can be configured as follows: + +[source, yaml] +.Access Log configuration file +---- +server: + port: 8080 + features: + access-log: + format: "%h %l %u %t %r %s %b %{Referer}i" +---- + +All options shown below are also available programmatically when using builder. + +include::{rootdir}/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc[leveloffset=+1] + == Supported Technologies == HTTP/2 Support @@ -624,138 +836,6 @@ curl --noproxy '*' -X POST -H "Content-Type: application/json" \ {"name":"Joe"} ---- -== Access Log - -Access logging in Helidon is done by a dedicated module that can be -added to WebServer and configured. - -Access logging is a Helidon WebServer `ServerFeature`. Access Log feature has a -very high weight, so it is registered before other features (such as security) that may -terminate a request. This is to ensure the log contains all requests with appropriate status codes. - -To enable Access logging add the following dependency to project's `pom.xml`: - -[source,xml] ----- - - io.helidon.webserver - helidon-webserver-access-log - ----- - - -=== Configuring Access Log in Your Code - -`AccessLogFeature` is discovered automatically by default, and configured through `server.features.access-log`. -You can also configure this feature in code by registering it with WebServer (which will replace the discovered feature). - -[source,java] ----- -include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_29, indent=0] ----- - -=== Configuring Access Log in a Configuration File - -Access log can be configured as follows: - -[source, yaml] -.Access Log configuration file ----- -server: - port: 8080 - features: - access-log: - format: "%h %l %u %t %r %s %b %{Referer}i" ----- - -All options shown below are also available programmatically when using builder. - -include::{rootdir}/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc[leveloffset=+1] - -== TLS Configuration - -Configure TLS either programmatically, or by the Helidon configuration framework. - -=== Configuring TLS in Your Code - -To configure TLS in WebServer programmatically create your keystore configuration and pass it to the WebServer builder. - -[source,java] ----- -include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_30, indent=0] ----- - - -=== Configuring TLS in the Config File - -It is also possible to configure TLS via the config file. - -[source,yaml] -.WebServer TLS configuration file `application.yaml` ----- -server: - tls: - #Truststore setup - trust: - keystore: - passphrase: "password" - trust-store: true - resource: - resource-path: "keystore.p12" - # Keystore with private key and server certificate - private-key: - keystore: - passphrase: "password" - resource: - resource-path: "keystore.p12" ----- -Then, in your application code, load the configuration from that file. - -[source,java] -.WebServer initialization using the `application.yaml` file located on the classpath ----- -include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_2, indent=0] ----- -<1> `application.yaml` is a default configuration source loaded when YAML support is on classpath, so we can -just use `Config.create()` -<2> Server expects the configuration tree located on the node of `server` - -Or you can only create WebServerTls instance based on the config file. - -[source,java] -.WebServerTls instance based on `application.yaml` file located on the classpath ----- -include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_31, indent=0] ----- - -This can alternatively be configured with paths to PKCS#8 PEM files rather than KeyStores: - -[source,yaml] -.WebServer TLS configuration file `application.yaml` ----- -server: - tls: - #Truststore setup - trust: - pem: - certificates: - resource: - resource-path: "ca-bundle.pem" - private-key: - pem: - key: - resource: - resource-path: "key.pem" - cert-chain: - resource: - resource-path: "chain.pem" ----- - -=== Configuration Options - -include::{rootdir}/config/io_helidon_common_tls_Tls.adoc[leveloffset=+2,tag=config] - - == HTTP Content Encoding HTTP encoding can improve bandwidth utilization and transfer speeds in certain scenarios. It diff --git a/webserver/cors/src/main/java/io/helidon/webserver/cors/CorsFeature.java b/webserver/cors/src/main/java/io/helidon/webserver/cors/CorsFeature.java index 22e70d25f5b..ccfdf92e228 100644 --- a/webserver/cors/src/main/java/io/helidon/webserver/cors/CorsFeature.java +++ b/webserver/cors/src/main/java/io/helidon/webserver/cors/CorsFeature.java @@ -34,7 +34,7 @@ public class CorsFeature implements Weighted, ServerFeature, RuntimeType.Api res.send("routing")); + updateRouting(routing, "routing-1", 0, FIRST); + updateRouting(routing, "routing-2", 0, SECOND); + } + + @Test + void testServiceRegistration() { + assertThat(client.get("/service") + .requestEntity(String.class), is("service:server-feature:1000")); + } + + @Test + void testServiceWithPathRegistration() { + assertThat(client.get("/path/service") + .requestEntity(String.class), is("service:server-feature:1000")); + } + + @Test + void testRouteRegistration() { + assertThat(client.get("/route") + .requestEntity(String.class), is("route:server-feature:1000")); + } + + @Test + void testErrorRegistration() { + assertThat(client.get("/error") + .requestEntity(String.class), is("error:server-feature:1000")); + } + + @Test + void testFilterRegistration() { + List filters = new CopyOnWriteArrayList<>(); + FIRST.filters(filters); + SECOND.filters(filters); + F_FILTER_1000.filters(filters); + F_FILTER_99.filters(filters); + HF_FILTER_999.filters(filters); + HF_FILTER_98.filters(filters); + + assertThat(client.get("/no-service") + .requestEntity(String.class), + is("routing")); + + /* + server-feature(1000) + http-feature(999) + routing-first(N/A) + routing-second(N/A) + server-feature(99) + http-feature(98) + */ + + /* + now the order of filers should be as follows: + F_FILTER_1000 - highest weight + HF_FILTER_999 + FIRST - default weight (registered through routing) + SECOND - dtto + F_FILTER_99 - lower than default, should be handled after default routing + HF_FILTER_98 + */ + assertThat("All 6 filters should have been called. Actual list: " + filters, filters.size(), is(6)); + assertThat("First should be filter from server feature, weight 1000. Actual list: " + filters, + filters.get(0), + sameInstance(F_FILTER_1000)); + assertThat("Second should be filter from HTTP feature, weight 999. Actual list: " + filters, + filters.get(1), + sameInstance(HF_FILTER_999)); + assertThat("Third should be first filter registered to routing. Actual list: " + filters, + filters.get(2), + sameInstance(FIRST)); + assertThat("Fourth should be second filter registered to routing. Actual list: " + filters, + filters.get(3), + sameInstance(SECOND)); + assertThat("Fifth should be filter from server feature, weight 99. Actual list: " + filters, + filters.get(4), + sameInstance(F_FILTER_99)); + assertThat("Last should be filter from HTTP feature, weight 98. Actual list: " + filters, + filters.get(5), + sameInstance(HF_FILTER_98)); + + } + + private static void updateRouting(HttpRouting.Builder routing, String type, int weight, Filter filter) { + routing.addFilter(filter) + .register(new TestHttpService(type, weight)) + .register("/path", new TestHttpService(type, weight)) + .route(HttpRoute.builder() + .path("/route") + .handler((req, res) -> res.send("route:" + type + ":" + weight)) + .build()) + .get("/error", (req, res) -> { + throw new TestException(type, weight); + }) + .error(TestException.class, (req, res, throwable) -> { + res.send("error:" + throwable.type + ":" + throwable.weight); + }); + } + + private static class TestHttpService implements HttpService { + private final String type; + private final int weight; + + private TestHttpService(String type, int weight) { + this.type = type; + this.weight = weight; + } + + @Override + public void routing(HttpRules rules) { + rules.get("/service", (req, res) -> res.send("service:" + type + ":" + weight)); + } + } + + private static class TestException extends RuntimeException { + private final String type; + private final int weight; + + private TestException(String type, int weight) { + this.type = type; + this.weight = weight; + } + } + + private static class TestFilter implements Filter { + private final String message; + private final int weight; + + private volatile List filters; + + private TestFilter(String message, int weight) { + this.message = message; + this.weight = weight; + } + + @Override + public void filter(FilterChain filterChain, RoutingRequest routingRequest, RoutingResponse routingResponse) { + if (filters != null) { + filters.add(this); + } + filterChain.proceed(); + } + + public void filters(List filters) { + this.filters = filters; + } + + @Override + public String toString() { + return message + "(" + weight + ")"; + } + } + + private static class TestHttpFeature implements HttpFeature, Weighted { + private final int weight; + private final TestFilter filter; + + private TestHttpFeature(int weight) { + this.weight = weight; + this.filter = new TestFilter("http-feature", weight); + } + + @Override + public void setup(HttpRouting.Builder routing) { + updateRouting(routing, "http-feature", weight, filter); + } + + @Override + public double weight() { + return weight; + } + + @Override + public String toString() { + return "http-feature(" + weight + ")"; + } + + TestFilter filter() { + return filter; + } + } + + private static class TestServerFeature implements ServerFeature, Weighted { + private final int weight; + private final TestFilter filter; + + private TestServerFeature(int weight) { + this.weight = weight; + this.filter = new TestFilter("server-feature", weight); + } + + @Override + public void setup(ServerFeatureContext featureContext) { + updateRouting(featureContext.socket(WebServer.DEFAULT_SOCKET_NAME) + .httpRouting(), "server-feature", weight, filter); + } + + @Override + public String name() { + return toString(); + } + + @Override + public String type() { + return toString(); + } + + @Override + public double weight() { + return weight; + } + + @Override + public String toString() { + return "server-feature(" + weight + ")"; + } + + TestFilter filter() { + return filter; + } + } +} diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java b/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java index cd8ce1c53fd..a9d0bebf800 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/LoomServer.java @@ -17,6 +17,7 @@ package io.helidon.webserver; import java.lang.management.ManagementFactory; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -35,7 +36,6 @@ import io.helidon.Main; import io.helidon.common.SerializationConfig; import io.helidon.common.Version; -import io.helidon.common.Weighted; import io.helidon.common.Weights; import io.helidon.common.context.Context; import io.helidon.common.features.HelidonFeatures; @@ -77,13 +77,13 @@ class LoomServer implements WebServer { sockets.put(DEFAULT_SOCKET_NAME, serverConfig); // features ordered by weight - List features = serverConfig.features(); + List features = new ArrayList<>(serverConfig.features()); + Weights.sort(features); + ServerFeatureContextImpl featureContext = ServerFeatureContextImpl.create(serverConfig); for (ServerFeature feature : features) { - featureContext.weight(Weights.find(feature, Weighted.DEFAULT_WEIGHT)); - feature.setup(featureContext); + featureContext.setUpFeature(feature); } - featureContext.weight(Weighted.DEFAULT_WEIGHT); Timer idleConnectionTimer = new Timer("helidon-idle-connection-timer", true); Map listenerMap = new HashMap<>(); diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/ServerFeatureContextImpl.java b/webserver/webserver/src/main/java/io/helidon/webserver/ServerFeatureContextImpl.java index 8372cea6fd7..afa0a79f0aa 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/ServerFeatureContextImpl.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/ServerFeatureContextImpl.java @@ -48,6 +48,7 @@ class ServerFeatureContextImpl implements ServerFeature.ServerFeatureContext { private final WebServerConfig serverConfig; private final Map socketToBuilders; private final Set configuredSockets; + private final Map inProgressBuilders; private final AtomicReference weight; @@ -61,6 +62,7 @@ private ServerFeatureContextImpl(WebServerConfig serverConfig, .filter(Predicate.not(DEFAULT_SOCKET_NAME::equals)) .collect(Collectors.toSet()); this.weight = weight; + this.inProgressBuilders = new HashMap<>(); } static ServerFeatureContextImpl create(WebServerConfig serverConfig) { @@ -73,7 +75,7 @@ static ServerFeatureContextImpl create(WebServerConfig serverConfig) { Map socketToBuilders = new HashMap<>(); socketToBuilders.put(DEFAULT_SOCKET_NAME, - new ListenerBuildersImpl(DEFAULT_SOCKET_NAME, serverConfig, httpRouting, routings, weight)); + new ListenerBuildersImpl(DEFAULT_SOCKET_NAME, serverConfig, httpRouting, routings)); // for each socket, gather all routing builders sockets.forEach((socketName, listener) -> { @@ -95,7 +97,7 @@ static ServerFeatureContextImpl create(WebServerConfig serverConfig) { builders.add(listenerHttpRouting); } socketToBuilders.put(socketName, - new ListenerBuildersImpl(socketName, serverConfig, listenerHttpRouting, builders, weight)); + new ListenerBuildersImpl(socketName, serverConfig, listenerHttpRouting, builders)); }); return new ServerFeatureContextImpl(serverConfig, socketToBuilders, weight); @@ -117,22 +119,32 @@ public Set sockets() { } @Override - public ListenerBuildersImpl socket(String socketName) { + public ServerFeature.SocketBuilders socket(String socketName) { return Optional.ofNullable(socketToBuilders.get(socketName)) + .map(it -> socketBuilderDelegate(socketName, it)) .orElseThrow(() -> new NoSuchElementException("There is no socket configuration for socket named \"" + socketName + "\"")); } - void weight(double weight) { - this.weight.set(weight); + void setUpFeature(ServerFeature feature) { + weight(Weights.find(feature, Weighted.DEFAULT_WEIGHT)); + /* + We need to create a routing that is specific to each feature, and wrap it as an HttpFeature, to honor weight + for cases, where a route, service, or a filter is added from a feature - it must be in the correct order + This will honor weights across Server features and HTTP features for webserver + */ + feature.setup(this); + inProgressBuilders.forEach(this::createHttpFeature); + inProgressBuilders.clear(); + weight(Weighted.DEFAULT_WEIGHT); } - Router router() { - return router(DEFAULT_SOCKET_NAME); + void weight(double weight) { + this.weight.set(weight); } Router router(String socketName) { - ListenerBuildersImpl listener = socket(socketName); + ListenerBuildersImpl listener = listenerBuilder(socketName); boolean containsHttp = listener.routings.stream() .anyMatch(it -> it instanceof HttpRouting.Builder); @@ -164,6 +176,54 @@ private static HttpRouting.Builder defaultRouting(WebServerConfig serverConfig) return httpRouting; } + private ServerFeature.SocketBuilders socketBuilderDelegate(String socketName, ListenerBuildersImpl listenerBuilders) { + return new ServerFeature.SocketBuilders() { + @Override + public ListenerConfig listener() { + return listenerBuilders.listener(); + } + + @Override + public HttpRouting.Builder httpRouting() { + return inProgressBuilders.computeIfAbsent(socketName, + it -> new ServerToHttpFeatureBuilder(weight.get(), + listenerBuilders.httpRouting())); + } + + @Override + public ServerFeature.RoutingBuilders routingBuilders() { + ServerFeature.RoutingBuilders delegate = listenerBuilders.routingBuilders(); + return new ServerFeature.RoutingBuilders() { + @Override + public boolean hasRouting(Class builderType) { + return delegate.hasRouting(builderType); + } + + @SuppressWarnings("unchecked") + @Override + public > T routingBuilder(Class builderType) { + if (builderType.equals(HttpRouting.class)) { + return (T) httpRouting(); + } + return delegate.routingBuilder(builderType); + } + }; + } + }; + } + + private ListenerBuildersImpl listenerBuilder(String socketName) { + return Optional.ofNullable(socketToBuilders.get(socketName)) + .orElseThrow(() -> new NoSuchElementException("There is no socket configuration for socket named \"" + + socketName + "\"")); + } + + private void createHttpFeature(String socket, ServerToHttpFeatureBuilder builder) { + socket(socket) + .httpRouting() + .addFeature(builder.toFeature()); + } + private static class RoutingBuildersImpl implements ServerFeature.RoutingBuilders { private final String socketName; private final Map, Object> buildersByType; @@ -202,18 +262,15 @@ private static class ListenerBuildersImpl implements ServerFeature.SocketBuilder private final ListenerConfig listenerConfig; private final HttpRouting.Builder httpRouting; private final List> routings; - private final AtomicReference weight; private final ServerFeature.RoutingBuilders routingBuilders; ListenerBuildersImpl(String socketName, ListenerConfig listenerConfig, HttpRouting.Builder httpRouting, - List> routings, - AtomicReference weight) { + List> routings) { this.listenerConfig = listenerConfig; this.httpRouting = httpRouting; this.routings = routings; - this.weight = weight; this.routingBuilders = RoutingBuildersImpl.create(socketName, routings); } diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/ServerToHttpFeatureBuilder.java b/webserver/webserver/src/main/java/io/helidon/webserver/ServerToHttpFeatureBuilder.java new file mode 100644 index 00000000000..051c16c275b --- /dev/null +++ b/webserver/webserver/src/main/java/io/helidon/webserver/ServerToHttpFeatureBuilder.java @@ -0,0 +1,128 @@ +/* + * 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.webserver; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import io.helidon.common.Weighted; +import io.helidon.webserver.http.ErrorHandler; +import io.helidon.webserver.http.Filter; +import io.helidon.webserver.http.HttpFeature; +import io.helidon.webserver.http.HttpRoute; +import io.helidon.webserver.http.HttpRouting; +import io.helidon.webserver.http.HttpSecurity; +import io.helidon.webserver.http.HttpService; +import io.helidon.webserver.http.Registration; + +class ServerToHttpFeatureBuilder implements HttpRouting.Builder { + private final List registrations = new ArrayList<>(); + + private final double weight; + private final HttpRouting.Builder delegate; + + ServerToHttpFeatureBuilder(double weight, HttpRouting.Builder delegate) { + this.weight = weight; + this.delegate = delegate; + } + + @Override + public HttpRouting.Builder register(HttpService... services) { + registrations.add(Registration.create(services)); + return this; + } + + @Override + public HttpRouting.Builder register(String path, HttpService... services) { + registrations.add(Registration.create(path, services)); + return this; + } + + @Override + public HttpRouting.Builder route(HttpRoute route) { + registrations.add(Registration.create(route)); + return this; + } + + @Override + public HttpRouting.Builder addFilter(Filter filter) { + registrations.add(Registration.create(filter)); + return this; + } + + @Override + public HttpRouting.Builder addFeature(Supplier feature) { + // features are always directly sent to delegate, as they have correct ordering based on weight + delegate.addFeature(feature); + return this; + } + + @Override + public HttpRouting.Builder error(Class exceptionClass, ErrorHandler handler) { + registrations.add(Registration.create(exceptionClass, handler)); + return this; + } + + @Override + public HttpRouting.Builder maxReRouteCount(int maxReRouteCount) { + registrations.add(Registration.createMaxRerouteCount(maxReRouteCount)); + return this; + } + + @Override + public HttpRouting.Builder security(HttpSecurity security) { + registrations.add(Registration.create(security)); + return this; + } + + @Override + public HttpRouting.Builder copy() { + ServerToHttpFeatureBuilder copy = new ServerToHttpFeatureBuilder(weight, delegate.copy()); + copy.registrations.addAll(this.registrations); + return copy; + } + + @Override + public HttpRouting build() { + throw new UnsupportedOperationException("This method should never escape internal Helidon types"); + } + + HttpFeature toFeature() { + return new HttpFeatureForServerFeature(this, weight); + } + + private static class HttpFeatureForServerFeature implements HttpFeature, Weighted { + private final ServerToHttpFeatureBuilder builder; + private final double weight; + + private HttpFeatureForServerFeature(ServerToHttpFeatureBuilder builder, double weight) { + this.builder = builder; + this.weight = weight; + } + + @Override + public void setup(HttpRouting.Builder routing) { + builder.registrations.forEach(it -> it.register(routing)); + } + + @Override + public double weight() { + return weight; + } + } +} diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http/HttpRoutingFeature.java b/webserver/webserver/src/main/java/io/helidon/webserver/http/HttpRoutingFeature.java index 698346ad4bf..275e9a62665 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http/HttpRoutingFeature.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http/HttpRoutingFeature.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. @@ -37,68 +37,28 @@ public void setup(HttpRouting.Builder routing) { } void filter(Filter filter) { - this.registrations.add(new FilterReg(filter)); + this.registrations.add(Registration.create(filter)); } void error(Class exceptionClass, ErrorHandler handler) { - this.registrations.add(new ErrorReg<>(exceptionClass, handler)); + this.registrations.add(Registration.create(exceptionClass, handler)); } void service(HttpService... services) { - this.registrations.add(new ServiceReg(services)); + this.registrations.add(Registration.create(services)); } void service(String path, HttpService... services) { - this.registrations.add(new ServicePathReg(path, services)); + this.registrations.add(Registration.create(path, services)); } void route(HttpRoute route) { - this.registrations.add(new RouteReg(route)); + this.registrations.add(Registration.create(route)); } void copyFrom(HttpRoutingFeature mainRouting) { this.registrations.addAll(mainRouting.registrations); } - private interface Registration { - void register(HttpRouting.Builder routing); - } - - private record FilterReg(Filter filter) implements Registration { - - @Override - public void register(HttpRouting.Builder routing) { - routing.addFilter(filter); - } - } - - private record ErrorReg(Class exceptionClass, ErrorHandler handler) - implements Registration { - - @Override - public void register(HttpRouting.Builder routing) { - routing.error(exceptionClass, handler); - } - } - - private record ServiceReg(HttpService[] services) implements Registration { - @Override - public void register(HttpRouting.Builder routing) { - routing.register(services); - } - } - private record ServicePathReg(String path, HttpService[] services) implements Registration { - @Override - public void register(HttpRouting.Builder routing) { - routing.register(path, services); - } - } - - private record RouteReg(HttpRoute route) implements Registration { - @Override - public void register(HttpRouting.Builder routing) { - routing.route(route); - } - } } diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http/Registration.java b/webserver/webserver/src/main/java/io/helidon/webserver/http/Registration.java new file mode 100644 index 00000000000..509e06ab7f1 --- /dev/null +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http/Registration.java @@ -0,0 +1,106 @@ +/* + * 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.webserver.http; + +/** + * A routing builder registration. + *

    + * This type is used internally in Helidon to allow gathering of registrations, and then re-applying them on a + * different builder. + */ +public interface Registration { + /** + * Create a registration for service(s) with a path. + * + * @param path path of the service(s) + * @param services service(s) to register + * @return a new registration + */ + static Registration create(String path, HttpService... services) { + return new Registrations.ServicePathRegistration(path, services); + } + + /** + * Create a registration for service(s). + * + * @param services service(s) to register + * @return a new registration + */ + static Registration create(HttpService... services) { + return new Registrations.ServiceRegistration(services); + } + + /** + * Create a registration for a route. + * + * @param route to register + * @return a new registration + */ + static Registration create(HttpRoute route) { + return new Registrations.RouteRegistration(route); + } + + /** + * Create a registration for a filter. + * + * @param filter to register + * @return a new registration + */ + static Registration create(Filter filter) { + return new Registrations.FilterRegistration(filter); + } + + /** + * Create a registration for an error handler. + * + * @param exceptionClass class of exception to map this handler to + * @param handler handler to handle that exception + * @param type of the exception to be handled + * @return a new registration + */ + static Registration create(Class exceptionClass, ErrorHandler handler) { + return new Registrations.ErrorRegistration<>(exceptionClass, handler); + } + + /** + * Create a registration for configuration of max re-route count. + * + * @param maxReRouteCount maximal number of re-routes to allow + * @return a new registration + */ + static Registration createMaxRerouteCount(int maxReRouteCount) { + return new Registrations.MaxRerouteCountRegistration(maxReRouteCount); + } + + /** + * Create a registration for HTTP security instance. + * + * @param security to register + * @return a new registration + */ + static Registration create(HttpSecurity security) { + return new Registrations.SecurityRegistration(security); + } + + /** + * Register this registration on a different routing builder. + * + * @param routing the routing builder to apply this registration on + */ + void register(HttpRouting.Builder routing); + +} diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http/Registrations.java b/webserver/webserver/src/main/java/io/helidon/webserver/http/Registrations.java new file mode 100644 index 00000000000..6078a19d965 --- /dev/null +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http/Registrations.java @@ -0,0 +1,115 @@ +/* + * 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.webserver.http; + +class Registrations { + static final class ServicePathRegistration implements Registration { + private final HttpService[] service; + private final String path; + + ServicePathRegistration(String path, HttpService... service) { + this.path = path; + this.service = service; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.register(path, service); + } + } + + static final class ServiceRegistration implements Registration { + private final HttpService[] service; + + ServiceRegistration(HttpService... service) { + this.service = service; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.register(service); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + static final class ErrorRegistration implements Registration { + private final Class exceptionClass; + private final ErrorHandler handler; + + ErrorRegistration(Class exceptionClass, ErrorHandler handler) { + this.exceptionClass = exceptionClass; + this.handler = handler; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.error(exceptionClass, handler); + } + } + + static final class RouteRegistration implements Registration { + private final HttpRoute route; + + RouteRegistration(HttpRoute route) { + this.route = route; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.route(route); + } + } + + static final class FilterRegistration implements Registration { + private final Filter filter; + + FilterRegistration(Filter filter) { + this.filter = filter; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.addFilter(filter); + } + } + + static final class MaxRerouteCountRegistration implements Registration { + private final int maxReRouteCount; + + MaxRerouteCountRegistration(int maxReRouteCount) { + this.maxReRouteCount = maxReRouteCount; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.maxReRouteCount(maxReRouteCount); + } + } + + static final class SecurityRegistration implements Registration { + private final HttpSecurity security; + + SecurityRegistration(HttpSecurity security) { + this.security = security; + } + + @Override + public void register(HttpRouting.Builder routing) { + routing.security(security); + } + } +} From 90d670e7ba7dbc6a53f58ae98f0f15f548ae3147 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Wed, 5 Jun 2024 12:15:16 +0200 Subject: [PATCH 041/245] 4.x - Archetype: fix Native image build for quickstart with jackson Signed-off-by: tvallin --- archetypes/archetypes/src/main/archetype/common/docker.xml | 6 ++++++ .../groupid/artifactid/native-image.properties.mustache | 2 +- .../xml/bind/jakarta.xml.bind-api/resource-config.json | 7 +++++++ archetypes/archetypes/src/main/archetype/common/media.xml | 3 +++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 archetypes/archetypes/src/main/archetype/common/files/src/main/resources/META-INF/native-image/jakarta/xml/bind/jakarta.xml.bind-api/resource-config.json diff --git a/archetypes/archetypes/src/main/archetype/common/docker.xml b/archetypes/archetypes/src/main/archetype/common/docker.xml index 5ed2e9376c8..2df7165a8c5 100644 --- a/archetypes/archetypes/src/main/archetype/common/docker.xml +++ b/archetypes/archetypes/src/main/archetype/common/docker.xml @@ -39,6 +39,12 @@ src/*/resources/META-INF/**/*.mustache + + files + + src/**/native-image/**/resource-config.json + + jakarta.xml.bind-api + + com.fasterxml.jackson.core + com.fasterxml.jackson.annotation com.fasterxml.jackson.core From 5ba18fb33aa8c3785ab6638da1d15b30e60149a4 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Wed, 5 Jun 2024 12:23:24 -0500 Subject: [PATCH 042/245] Fix tracer information propagation across threads using Helidon context (#8841) * Fix context propagation Signed-off-by: Tim Quinn * Add comments to new test and reformat its source a bit --------- Signed-off-by: Tim Quinn --- .../tests/TestTracerAndSpanPropagation.java | 137 ++++++++++++++++++ .../src/main/java/module-info.java | 1 + .../OpenTelemetryDataPropagationProvider.java | 15 +- tracing/providers/zipkin/pom.xml | 13 ++ 4 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 tracing/provider-tests/src/main/java/io/helidon/tracing/providers/tests/TestTracerAndSpanPropagation.java diff --git a/tracing/provider-tests/src/main/java/io/helidon/tracing/providers/tests/TestTracerAndSpanPropagation.java b/tracing/provider-tests/src/main/java/io/helidon/tracing/providers/tests/TestTracerAndSpanPropagation.java new file mode 100644 index 00000000000..32b9524f3e0 --- /dev/null +++ b/tracing/provider-tests/src/main/java/io/helidon/tracing/providers/tests/TestTracerAndSpanPropagation.java @@ -0,0 +1,137 @@ +/* + * 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.tests; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import io.helidon.common.context.Context; +import io.helidon.common.context.Contexts; +import io.helidon.common.testing.junit5.OptionalMatcher; +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.sameInstance; + +/** + * Tests that the current tracer and span are propagated through Helidon contexts. + *

    + * Derived as closely as possible from a user-supplied reproducer. Basically, logging was changed to use system + * logging instead of SLF4J as in the original test and test assertions were converted to use Hamcrest. The key logic + * in the test is unchanged. + *

    + */ +class TestTracerAndSpanPropagation { + private static final System.Logger LOGGER = System + .getLogger(TestTracerAndSpanPropagation.class.getName()); + + @Test + void concurrentVirtualThreadUseCanary() { + Contexts.runInContext(Context.create(), this::actualTest); + } + + private void actualTest() { + + final var tracer = buildTracer(); + LOGGER.log(System.Logger.Level.INFO, "Tracer {0}", tracer); + assertThat("Tracer is enabled", tracer.enabled(), is(true)); + assertThat("Tracer in use is the global tracer", tracer, sameInstance(Tracer.global())); + final var rootSpan = tracer.spanBuilder(getClass().getSimpleName()).start(); + LOGGER.log(System.Logger.Level.INFO, "traceId: {0}", rootSpan.context().traceId()); + try (var ignored = rootSpan.activate()) { + assertThat("rootSpan activate", + Span.current().map(Span::context).map(SpanContext::spanId), + OptionalMatcher.optionalValue(is(rootSpan.context().spanId()))); + + final var ff = new CompletableFuture[1]; + try (var executor = Contexts.wrap(Executors.newVirtualThreadPerTaskExecutor())) { + final var futures = new CompletableFuture[5]; + for (int i = 0; i < 5; i++) { + futures[i] = CompletableFuture + .runAsync(new ChildAction(tracer, rootSpan), executor); + } + for (final var f : futures) { + ff[0] = f; + f.get(1, TimeUnit.SECONDS); + } + LOGGER.log(System.Logger.Level.INFO, "all futures complete"); + + } catch (ExecutionException | TimeoutException | InterruptedException e) { + LOGGER.log(System.Logger.Level.ERROR, "Failure: f={0}", ff[0], e); + rootSpan.end(e); + throw new RuntimeException(e); + } + rootSpan.end(); + LOGGER.log(System.Logger.Level.INFO, "ended rootSpan"); + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + LOGGER.log(System.Logger.Level.ERROR, "Error on sleep", e); + throw new RuntimeException(e); + } + LOGGER.log(System.Logger.Level.INFO, "test ended"); + } + + private Tracer buildTracer() { + return Tracer.global(); + } + + record ChildAction(Tracer globalTracer, Span rootSpan) implements Runnable { + + @Override + public void run() { + final var thread = Thread.currentThread(); + assertThat("isVirtual false in ChildAction", thread.isVirtual(), is(true)); + final var threadName = String.valueOf(thread); + LOGGER.log(System.Logger.Level.INFO, "Running {0}", threadName); + + final var tracer = Objects.requireNonNull(Tracer.global(), "global NOT in ChildAction"); + assertThat("tracer NOT preserved in ChildAction", tracer, is(sameInstance(globalTracer))); + assertThat("Current span from test NOT present in ChildAction", + Span.current().map(Span::context).map(SpanContext::spanId), + OptionalMatcher.optionalValue(is(rootSpan.context().spanId()))); + + final var span = Span.current().get(); + final var spanContext = span.context(); + final var childSpan = tracer.spanBuilder(threadName) + .parent(spanContext).start(); + try (var ignored = childSpan.activate()) { + + assertThat("childSpan NOT activated", + Span.current().map(Span::context).map(SpanContext::spanId), + OptionalMatcher.optionalValue(is(childSpan.context().spanId()))); + Thread.sleep(10); + span.end(); + + } catch (InterruptedException e) { + span.end(e); + throw new RuntimeException(e); + } finally { + LOGGER.log(System.Logger.Level.INFO, "Ended {0}", threadName); + } + } + } +} diff --git a/tracing/provider-tests/src/main/java/module-info.java b/tracing/provider-tests/src/main/java/module-info.java index 8a90d893769..dd4c2b97018 100644 --- a/tracing/provider-tests/src/main/java/module-info.java +++ b/tracing/provider-tests/src/main/java/module-info.java @@ -21,6 +21,7 @@ requires java.logging; requires io.helidon.tracing; + requires io.helidon.common.context; requires io.helidon.common.testing.junit5; requires org.junit.jupiter.api; 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 index 2c7c9561b3f..014c1283c32 100644 --- 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 @@ -32,10 +32,15 @@ public class OpenTelemetryDataPropagationProvider @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); + // Use a tracer from the current context--because there is no notion in OTel of a "current" tracer--or from the + // global tracer. + Tracer tracer = Contexts.context().flatMap(ctx -> ctx.get(Tracer.class)).orElseGet(Tracer::global); + + // Get the current span only from OTel's notion of the current span. We do not care what span might be set in the + // current context, because after that context was constructed user code could have closed the current span or set + // a new span as current. + Span span = OpenTelemetryTracerProvider.activeSpan().orElse(null); + return new OpenTelemetryContext(tracer, span); } @Override @@ -51,7 +56,7 @@ public void clearData(OpenTelemetryContext context) { @Override public void propagateData(OpenTelemetryContext context) { - if (context != null) { + if (context != null && context.span != null) { context.scope = context.span.activate(); } } diff --git a/tracing/providers/zipkin/pom.xml b/tracing/providers/zipkin/pom.xml index 2d0a706d4ce..23696883623 100644 --- a/tracing/providers/zipkin/pom.xml +++ b/tracing/providers/zipkin/pom.xml @@ -115,6 +115,19 @@
    + + + org.apache.maven.plugins + maven-surefire-plugin + + + io.helidon.tracing.providers.tests.TestTracerAndSpanPropagation.java + + + From abba8b5eebc0776eb2f13e64aef6d9b224773b88 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Wed, 5 Jun 2024 22:37:24 +0200 Subject: [PATCH 043/245] 4.x: Mocking documentation (#8787) * Mock documentation * Helidon mock documentation --------- Signed-off-by: tvallin --- docs/pom.xml | 4 + .../asciidoc/mp/guides/testing-junit5.adoc | 2 +- .../asciidoc/mp/{ => testing}/testing-ng.adoc | 6 +- .../asciidoc/mp/{ => testing}/testing.adoc | 72 +++++++++++- docs/src/main/asciidoc/sitegen.yaml | 1 + .../docs/mp/testing/CDIMockingSnippets.java | 104 ++++++++++++++++++ .../mp/testing/HelidonMockingSnippets.java | 78 +++++++++++++ .../mp/{ => testing}/TestingNgSnippets.java | 2 +- .../mp/{ => testing}/TestingSnippets.java | 2 +- 9 files changed, 262 insertions(+), 9 deletions(-) rename docs/src/main/asciidoc/mp/{ => testing}/testing-ng.adoc (97%) rename docs/src/main/asciidoc/mp/{ => testing}/testing.adoc (72%) create mode 100644 docs/src/main/java/io/helidon/docs/mp/testing/CDIMockingSnippets.java create mode 100644 docs/src/main/java/io/helidon/docs/mp/testing/HelidonMockingSnippets.java rename docs/src/main/java/io/helidon/docs/mp/{ => testing}/TestingNgSnippets.java (98%) rename docs/src/main/java/io/helidon/docs/mp/{ => testing}/TestingSnippets.java (98%) diff --git a/docs/pom.xml b/docs/pom.xml index 5023ef83da4..50387533dc3 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -43,6 +43,10 @@ pom true + + org.mockito + mockito-core + org.apache.activemq diff --git a/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc b/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc index 4a527b50bc6..876eef808a0 100644 --- a/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc +++ b/docs/src/main/asciidoc/mp/guides/testing-junit5.adoc @@ -177,7 +177,7 @@ This guide demonstrated how to create tests for MicroProfile applications in a J Refer to the following references for additional information: * https://junit.org/junit5/docs/current/user-guide/[JUnit 5 User Guide] -* xref:../testing.adoc[Testing with JUnit 5] +* xref:../testing/testing.adoc[Testing with JUnit 5] diff --git a/docs/src/main/asciidoc/mp/testing-ng.adoc b/docs/src/main/asciidoc/mp/testing/testing-ng.adoc similarity index 97% rename from docs/src/main/asciidoc/mp/testing-ng.adoc rename to docs/src/main/asciidoc/mp/testing/testing-ng.adoc index a4f10e31ec4..f21f7869bf1 100644 --- a/docs/src/main/asciidoc/mp/testing-ng.adoc +++ b/docs/src/main/asciidoc/mp/testing/testing-ng.adoc @@ -20,7 +20,7 @@ :description: Helidon Testing with TestNG :keywords: helidon, mp, test, testing, testng :feature-name: Testing with TestNG -:rootdir: {docdir}/.. +:rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] @@ -128,7 +128,7 @@ In the current example, Helidon container will be launched prior test. The _Bean [source,java] .Code sample ---- -include::{sourcedir}/mp/TestingNgSnippets.java[tag=snippet_1, indent=0] +include::{sourcedir}/mp/testing/TestingNgSnippets.java[tag=snippet_1, indent=0] ---- <1> Start the Helidon container. <2> Set disabled Bean Discovery for the current test class. @@ -143,7 +143,7 @@ To test `@RequestScoped` bean with JaxRs support: [source,java] .Test `RequestScoped` bean ---- -include::{sourcedir}/mp/TestingNgSnippets.java[tag=snippet_2, indent=0] +include::{sourcedir}/mp/testing/TestingNgSnippets.java[tag=snippet_2, indent=0] ---- <1> Start the Helidon container. <2> Set disabled Bean discovery. diff --git a/docs/src/main/asciidoc/mp/testing.adoc b/docs/src/main/asciidoc/mp/testing/testing.adoc similarity index 72% rename from docs/src/main/asciidoc/mp/testing.adoc rename to docs/src/main/asciidoc/mp/testing/testing.adoc index 28ff6c10a43..da0895b3fb7 100644 --- a/docs/src/main/asciidoc/mp/testing.adoc +++ b/docs/src/main/asciidoc/mp/testing/testing.adoc @@ -22,7 +22,7 @@ :description: Helidon Testing with JUnit5 :keywords: helidon, mp, test, testing, junit :feature-name: Testing with JUnit -:rootdir: {docdir}/.. +:rootdir: {docdir}/../.. include::{rootdir}/includes/mp.adoc[] @@ -32,6 +32,7 @@ include::{rootdir}/includes/mp.adoc[] - <> - <> - <> +- <> - <> - <> @@ -136,7 +137,7 @@ In the current example, Helidon container will be launched prior test. The _Bean [source,java] .Code sample ---- -include::{sourcedir}/mp/TestingSnippets.java[tag=snippet_1, indent=0] +include::{sourcedir}/mp/testing/TestingSnippets.java[tag=snippet_1, indent=0] ---- <1> Start the Helidon container. <2> Set disabled Bean Discovery for the current test class. @@ -151,13 +152,78 @@ To test `@RequestScoped` bean with JaxRs support: [source,java] .Test `RequestScoped` bean ---- -include::{sourcedir}/mp/TestingSnippets.java[tag=snippet_2, indent=0] +include::{sourcedir}/mp/testing/TestingSnippets.java[tag=snippet_2, indent=0] ---- <1> Start the Helidon container. <2> Set disabled Bean discovery. <3> Add JaxRs support to the current test class. <4> Define a `RequestScoped` bean. +== Mock Support + +This section describes how to mock objects using Helidon API and in a second phase, using pure CDI. + +=== Helidon Mock Support + +Helidon has its own API to use mocking with test classes annotated with `@HelidonTest`. + +==== Maven Coordinates + +To enable Helidon Mock Support add the following dependency to your project’s pom.xml. +[source,xml] +---- + + io.helidon.microprofile.testing + helidon-microprofile-testing-mocking + test + +---- + +==== API + +It consists of one annotation named `@MockBean`, designed to be used on fields and parameters. The implementation +relies only on CDI and thus it works with either JUnit or TestNG. The annotation has a parameter `answers` used +to set the default answer for the mocked beans. + +==== Example + +[source,java] +.Code sample +---- +include::{sourcedir}/mp/testing/HelidonMockingSnippets.java[tag=snippet_1, indent=0] +---- +<1> `service` field annotated with `@MockBean` and `Answers.CALLS_REAL_METHODS` for default answers. +<2> Test the mocked service with real method response. +<3> Test the mocked service with modified behavior. + +=== Mocking objects with pure CDI + +CDI can be used to enable mocking, the following example shows how to mock a service and inject it in a JAX-RS resource. +Let's consider a simple service `FooService` with a dummy method `FooService#getFoo` that return `foo` as a `String`. +And a resource where the service is injected and expose an endpoint at `/foo` that get the `String` provided by the service. + +[source,java] +.Code sample +---- +include::{sourcedir}/mp/testing/CDIMockingSnippets.java[tag=snippet_1, indent=0] +---- +<1> A simple `foo` Service. +<2> Inject the service into the resource. + +This example uses Mockito to create mock instances before each test. The method mockFooService is a CDI producer method +that adds the mock instance as an @Alternative in order to replace the FooService singleton. + +[source,java] +.Code sample +---- +include::{sourcedir}/mp/testing/CDIMockingSnippets.java[tag=snippet_2, indent=0] +---- +<1> Set priority to 1 because of `@Alternative`. +<2> Set `fooService` to a new mock before each tests. +<3> This makes `FooResource` inject the mock instead of the default singleton. +<4> Test that the mock is injected with modified behavior. +<5> Test the real method behavior. + == Additional Information * https://medium.com/helidon/testing-helidon-9df2ea14e22[Official blog article about Helidon and JUnit usage] diff --git a/docs/src/main/asciidoc/sitegen.yaml b/docs/src/main/asciidoc/sitegen.yaml index 865fd227e74..35f50b37b33 100644 --- a/docs/src/main/asciidoc/sitegen.yaml +++ b/docs/src/main/asciidoc/sitegen.yaml @@ -277,6 +277,7 @@ backend: value: "analytics" - type: "MENU" title: "Testing" + dir: "testing" glyph: type: "icon" value: "thumbs_up_down" diff --git a/docs/src/main/java/io/helidon/docs/mp/testing/CDIMockingSnippets.java b/docs/src/main/java/io/helidon/docs/mp/testing/CDIMockingSnippets.java new file mode 100644 index 00000000000..a0841f7edc3 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/testing/CDIMockingSnippets.java @@ -0,0 +1,104 @@ +/* + * 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.docs.mp.testing; + +import io.helidon.microprofile.testing.junit5.HelidonTest; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Answers; +import org.mockito.Mockito; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.when; + +@SuppressWarnings("ALL") +class CDIMockingSnippets { + + // tag::snippet_1[] + @ApplicationScoped + public class FooService { // <1> + + public String getFoo() { + return "foo"; + } + } + + @Path("/foo") + public class FooResource { + + @Inject + private FooService fooService; // <2> + + @GET + public String getFoo() { + return fooService.getFoo(); + } + } + // end::snippet_1[] + + // tag::snippet_2[] + @HelidonTest + @Priority(1) // <1> + class FooTest { + + @Inject + private WebTarget target; + + private FooService fooService; + + @BeforeEach + void initMock() { + fooService = Mockito.mock(FooService.class, Answers.CALLS_REAL_METHODS); // <2> + } + + @Produces + @Alternative + FooService mockFooService() { + return fooService; // <3> + } + + @Test + void testMockedService() { + when(fooService.getFoo()).thenReturn("bar"); // <4> + + Response response = target.path("/foo").request().get(); + + assertThat(response.getStatus(), is(200)); + assertThat(response.readEntity(String.class), is("bar")); + } + + @Test + void testService() { + Response response = target.path("/foo").request().get(); // <5> + + assertThat(response.getStatus(), is(200)); + assertThat(response.readEntity(String.class), is("foo")); + } + } + // end::snippet_2[] +} diff --git a/docs/src/main/java/io/helidon/docs/mp/testing/HelidonMockingSnippets.java b/docs/src/main/java/io/helidon/docs/mp/testing/HelidonMockingSnippets.java new file mode 100644 index 00000000000..0ad2cb5cbbe --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/testing/HelidonMockingSnippets.java @@ -0,0 +1,78 @@ +/* + * 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.docs.mp.testing; + +import io.helidon.microprofile.testing.junit5.AddBean; +import io.helidon.microprofile.testing.junit5.HelidonTest; +import io.helidon.microprofile.testing.mocking.MockBean; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.WebTarget; + +import org.junit.jupiter.api.Test; +import org.mockito.Answers; +import org.mockito.Mockito; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@SuppressWarnings("ALL") +class HelidonMockingSnippets { + + // tag::snippet_1[] + @HelidonTest + @AddBean(MockBeanAnswerTest.Resource.class) + @AddBean(MockBeanAnswerTest.Service.class) + class MockBeanAnswerTest { + + @MockBean(answer = Answers.CALLS_REAL_METHODS) // <1> + private Service service; + @Inject + private WebTarget target; + + @Test + void injectionTest() { + String response = target.path("/test").request().get(String.class); + assertThat(response, is("Not Mocked")); // <2> + Mockito.when(service.test()).thenReturn("Mocked"); + response = target.path("/test").request().get(String.class); + assertThat(response, is("Mocked")); // <3> + } + + @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"; + } + + } + } + // end::snippet_1[] +} diff --git a/docs/src/main/java/io/helidon/docs/mp/TestingNgSnippets.java b/docs/src/main/java/io/helidon/docs/mp/testing/TestingNgSnippets.java similarity index 98% rename from docs/src/main/java/io/helidon/docs/mp/TestingNgSnippets.java rename to docs/src/main/java/io/helidon/docs/mp/testing/TestingNgSnippets.java index f39734ef1cf..d696e44cade 100644 --- a/docs/src/main/java/io/helidon/docs/mp/TestingNgSnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/testing/TestingNgSnippets.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.helidon.docs.mp; +package io.helidon.docs.mp.testing; import io.helidon.microprofile.config.ConfigCdiExtension; import io.helidon.microprofile.testing.testng.AddBean; diff --git a/docs/src/main/java/io/helidon/docs/mp/TestingSnippets.java b/docs/src/main/java/io/helidon/docs/mp/testing/TestingSnippets.java similarity index 98% rename from docs/src/main/java/io/helidon/docs/mp/TestingSnippets.java rename to docs/src/main/java/io/helidon/docs/mp/testing/TestingSnippets.java index a7dfc2d39fc..919d2062a2c 100644 --- a/docs/src/main/java/io/helidon/docs/mp/TestingSnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/testing/TestingSnippets.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.helidon.docs.mp; +package io.helidon.docs.mp.testing; import io.helidon.microprofile.config.ConfigCdiExtension; import io.helidon.microprofile.testing.junit5.AddBean; From b091bee0f7f9e1cdbd992a6e5f6691c932167ad0 Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Thu, 6 Jun 2024 09:02:39 +0200 Subject: [PATCH 044/245] HTTP2-Settings needs to be encoded/decoded to Base64 with url dialect #8399 (#8845) Signed-off-by: Daniel Kec --- .../http2/Http2ClientConnectionHandler.java | 4 +- .../webserver/http2/Http2Connection.java | 7 +- .../webserver/http2/Http2Upgrader.java | 22 +++- .../webserver/http2/UpgradeSettingsTest.java | 108 ++++++++++++++++++ 4 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 webserver/http2/src/test/java/io/helidon/webserver/http2/UpgradeSettingsTest.java diff --git a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnectionHandler.java b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnectionHandler.java index 539b1a378f6..abb5a8f25dd 100644 --- a/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnectionHandler.java +++ b/webclient/http2/src/main/java/io/helidon/webclient/http2/Http2ClientConnectionHandler.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. @@ -208,7 +208,7 @@ private String settingsForUpgrade(Http2ClientProtocolConfig protocolConfig) { .data(); byte[] b = new byte[settingsFrameData.available()]; settingsFrameData.read(b); - return Base64.getEncoder().encodeToString(b); + return Base64.getUrlEncoder().encodeToString(b); } private Http2ConnectionAttemptResult http1(Http2ClientImpl http2Client, diff --git a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Connection.java b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Connection.java index 3713d68f4e6..fa70fa15fb9 100644 --- a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Connection.java +++ b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Connection.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. @@ -321,6 +321,11 @@ Http2Settings serverSettings() { return serverSettings; } + // jUnit Http2Settings pkg only visible test accessor. + Http2Settings clientSettings() { + return clientSettings; + } + private void doHandle(Semaphore requestSemaphore) throws InterruptedException { myThread = Thread.currentThread(); while (canRun && state != State.FINISHED) { diff --git a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Upgrader.java b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Upgrader.java index 8ec43b0bc5d..23209f273e2 100644 --- a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Upgrader.java +++ b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2Upgrader.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. @@ -43,7 +43,6 @@ public class Http2Upgrader implements Http1Upgrader { + "Upgrade: h2c\r\n\r\n") .getBytes(StandardCharsets.UTF_8); private static final HeaderName HTTP2_SETTINGS_HEADER_NAME = HeaderNames.create("HTTP2-Settings"); - private static final Base64.Decoder BASE_64_DECODER = Base64.getDecoder(); private final Http2Config config; private final List subProtocolProviders; @@ -77,8 +76,7 @@ public ServerConnection upgrade(ConnectionContext ctx, WritableHeaders headers) { Http2Connection connection = new Http2Connection(ctx, config, subProtocolProviders); if (headers.contains(HTTP2_SETTINGS_HEADER_NAME)) { - connection.clientSettings(Http2Settings.create(BufferData.create(BASE_64_DECODER.decode(headers.get( - HTTP2_SETTINGS_HEADER_NAME).value().getBytes(StandardCharsets.US_ASCII))))); + connection.clientSettings(token68ToHttp2Settings(headers.get(HTTP2_SETTINGS_HEADER_NAME).valueBytes())); } else { throw new RuntimeException("Bad request -> not " + HTTP2_SETTINGS_HEADER_NAME + " header"); } @@ -86,7 +84,7 @@ public ServerConnection upgrade(ConnectionContext ctx, http2Headers.path(prologue.uriPath().rawPath()); http2Headers.method(prologue.method()); headers.remove(HeaderNames.HOST, - it -> http2Headers.authority(it.value())); + it -> http2Headers.authority(it.get())); http2Headers.scheme("http"); // TODO need to get if https (ctx)? HttpPrologue newPrologue = HttpPrologue.create(Http2Connection.FULL_PROTOCOL, @@ -104,4 +102,18 @@ public ServerConnection upgrade(ConnectionContext ctx, return connection; } + /** + * RFC7540 3.2.1. + *
    {@code
    +     * HTTP2-Settings    = token68
    +     * token68           = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
    +     * }
    + * + * @param bytes Base64URL encoded bytes + * @return HTTP/2 settings + */ + private static Http2Settings token68ToHttp2Settings(byte[] bytes) { + return Http2Settings.create(BufferData.create(Base64.getUrlDecoder().decode(bytes))); + } + } diff --git a/webserver/http2/src/test/java/io/helidon/webserver/http2/UpgradeSettingsTest.java b/webserver/http2/src/test/java/io/helidon/webserver/http2/UpgradeSettingsTest.java new file mode 100644 index 00000000000..409350b646e --- /dev/null +++ b/webserver/http2/src/test/java/io/helidon/webserver/http2/UpgradeSettingsTest.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.webserver.http2; + +import java.util.Base64; + +import io.helidon.common.buffers.BufferData; +import io.helidon.common.buffers.DataWriter; +import io.helidon.http.HeaderValues; +import io.helidon.http.HttpPrologue; +import io.helidon.http.Method; +import io.helidon.http.WritableHeaders; +import io.helidon.http.http2.Http2Flag; +import io.helidon.http.http2.Http2Settings; +import io.helidon.webserver.ConnectionContext; +import io.helidon.webserver.ListenerContext; +import io.helidon.webserver.Router; + +import org.junit.jupiter.api.Test; + +import static io.helidon.http.http2.Http2Setting.ENABLE_PUSH; +import static io.helidon.http.http2.Http2Setting.HEADER_TABLE_SIZE; +import static io.helidon.http.http2.Http2Setting.INITIAL_WINDOW_SIZE; +import static io.helidon.http.http2.Http2Setting.MAX_CONCURRENT_STREAMS; +import static io.helidon.http.http2.Http2Setting.MAX_FRAME_SIZE; +import static io.helidon.http.http2.Http2Setting.MAX_HEADER_LIST_SIZE; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class UpgradeSettingsTest { + + static final long MAX_UNSIGNED_INT = 0xFFFFFFFFL; + + private final ConnectionContext ctx; + private final HttpPrologue prologue; + + public UpgradeSettingsTest() { + ctx = mock(ConnectionContext.class); + prologue = HttpPrologue.create("http/1.1", + "http", + "1.1", + Method.GET, + "/resource.txt", + false); + DataWriter dataWriter = mock(DataWriter.class); + when(ctx.router()).thenReturn(Router.empty()); + when(ctx.listenerContext()).thenReturn(mock(ListenerContext.class)); + when(ctx.dataWriter()).thenReturn(dataWriter); + } + + @Test + void urlEncodedSettingsGH8399() { + Http2Settings s = upgrade("AAEAABAAAAIAAAABAAN_____AAQAAP__AAUAAEAAAAYAACAA"); + assertThat(s.presentValue(HEADER_TABLE_SIZE).orElseThrow(), is(4096L)); + assertThat(s.presentValue(ENABLE_PUSH).orElseThrow(), is(true)); + assertThat(s.presentValue(MAX_CONCURRENT_STREAMS).orElseThrow(), is(MAX_UNSIGNED_INT / 2)); + assertThat(s.presentValue(INITIAL_WINDOW_SIZE).orElseThrow(), is(65_535L)); + assertThat(s.presentValue(MAX_FRAME_SIZE).orElseThrow(), is(16_384L)); + assertThat(s.presentValue(MAX_HEADER_LIST_SIZE).orElseThrow(), is(8192L)); + } + + @Test + void urlEncodedSettings() { + Http2Settings settings2 = Http2Settings.builder() + .add(HEADER_TABLE_SIZE, 4096L) + .add(ENABLE_PUSH, false) + .add(MAX_CONCURRENT_STREAMS, MAX_UNSIGNED_INT - 5) + .add(INITIAL_WINDOW_SIZE, 65535L) + .add(MAX_FRAME_SIZE, 16384L) + .add(MAX_HEADER_LIST_SIZE, 256L) + .build(); + String encSett = Base64.getUrlEncoder().encodeToString(settingsToBytes(settings2)); + Http2Settings s = upgrade(encSett); + assertThat(s.presentValue(MAX_CONCURRENT_STREAMS).orElseThrow(), is(MAX_UNSIGNED_INT - 5)); + assertThat(s.presentValue(MAX_HEADER_LIST_SIZE).orElseThrow(), is(256L)); + } + + Http2Settings upgrade(String http2Settings) { + WritableHeaders headers = WritableHeaders.create().add(HeaderValues.create("HTTP2-Settings", http2Settings)); + Http2Upgrader http2Upgrader = Http2Upgrader.create(Http2Config.create()); + Http2Connection connection = (Http2Connection) http2Upgrader.upgrade(ctx, prologue, headers); + return connection.clientSettings(); + } + + byte[] settingsToBytes(Http2Settings settings) { + BufferData settingsFrameData = + settings.toFrameData(null, 0, Http2Flag.SettingsFlags.create(0)).data(); + byte[] b = new byte[settingsFrameData.available()]; + settingsFrameData.read(b); + return b; + } +} From e4f997b4ab878326edceeaf90bba6136ec448c23 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Thu, 6 Jun 2024 12:27:28 -0500 Subject: [PATCH 045/245] Reorder checking of delegate vs. wrapper in OTel tracer unwrap (#8855) * Reorder checking of delegate vs. wrapper in OTel tracer unwrap Signed-off-by: Tim Quinn * Relax the test a bit making sure unwrap(Object.class) returns what we expect --------- Signed-off-by: Tim Quinn --- .../providers/opentelemetry/OpenTelemetryTracer.java | 7 ++++--- .../tracing/providers/opentelemetry/TestUnwrap.java | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryTracer.java b/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryTracer.java index f38bb09cadd..d50c67c3700 100644 --- a/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryTracer.java +++ b/tracing/providers/opentelemetry/src/main/java/io/helidon/tracing/providers/opentelemetry/OpenTelemetryTracer.java @@ -96,12 +96,13 @@ public void inject(SpanContext spanContext, HeaderProvider inboundHeadersProvide @Override public T unwrap(Class tracerClass) { - if (tracerClass.isInstance(this)) { - return tracerClass.cast(this); - } if (tracerClass.isAssignableFrom(delegate.getClass())) { return tracerClass.cast(delegate); } + if (tracerClass.isInstance(this)) { + return tracerClass.cast(this); + } + throw new IllegalArgumentException("Cannot provide an instance of " + tracerClass.getName() + ", telemetry tracer is: " + delegate.getClass().getName()); } diff --git a/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestUnwrap.java b/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestUnwrap.java index d654364844f..387f2e8e125 100644 --- a/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestUnwrap.java +++ b/tracing/providers/opentelemetry/src/test/java/io/helidon/tracing/providers/opentelemetry/TestUnwrap.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; class TestUnwrap { @@ -32,6 +33,14 @@ void testTracer() { assertThat("Tracer unwrapped", tracer.unwrap(Tracer.class), instanceOf(Tracer.class)); + + assertThat("Delegate unwrapped", + tracer.unwrap(io.opentelemetry.api.trace.Tracer.class), + instanceOf(io.opentelemetry.api.trace.Tracer.class)); + + assertThat("Object.toString()", + tracer.unwrap(Object.class).toString(), + containsString("io.opentelemetry")); } @Test From 97bcf73719bcd585e4bf81ec463a14874b10b56c Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 7 Jun 2024 14:32:51 +0200 Subject: [PATCH 046/245] 4.x: Update generated config reference (#8852) * Config metatadata: Handle enum types better for default value Javadoc processing in a separate class to make it more readable * Config reference docs: Fixed tables in option descriptions No longer displaying `@return`, `@see` in option descriptions --- .../config/metadata/processor/Javadoc.java | 101 ++++++++++++++++++ .../metadata/processor/TypeHandlerBase.java | 62 ++++++----- .../processor/TypeHandlerBuilderApi.java | 15 ++- .../asciidoc/config/config_reference.adoc | 23 ++-- ...helidon_common_configurable_AllowList.adoc | 20 ++-- ..._helidon_common_configurable_LruCache.adoc | 6 +- ..._helidon_common_configurable_Resource.adoc | 20 ++-- ...onfigurable_ScheduledThreadPoolConfig.adoc | 20 ++-- ...figurable_ScheduledThreadPoolSupplier.adoc | 20 ++-- ..._common_configurable_ThreadPoolConfig.adoc | 42 ++++---- ...ommon_configurable_ThreadPoolSupplier.adoc | 42 ++++---- .../config/io_helidon_common_pki_Keys.adoc | 10 +- .../io_helidon_common_pki_KeystoreKeys.adoc | 20 ++-- .../config/io_helidon_common_pki_PemKeys.adoc | 12 +-- ...o_helidon_common_socket_SocketOptions.adoc | 20 ++-- ...o_helidon_common_tls_RevocationConfig.adoc | 19 ++-- .../config/io_helidon_common_tls_Tls.adoc | 52 ++++----- ...on_dbclient_jdbc_JdbcParametersConfig.adoc | 32 +++--- .../io_helidon_faulttolerance_Async.adoc | 4 +- .../io_helidon_faulttolerance_Bulkhead.adoc | 6 +- ...helidon_faulttolerance_CircuitBreaker.adoc | 18 ++-- .../io_helidon_faulttolerance_Retry.adoc | 16 +-- .../io_helidon_faulttolerance_Timeout.adoc | 6 +- ...don_http_RequestedUriDiscoveryContext.adoc | 6 +- ..._http_encoding_ContentEncodingContext.adoc | 4 +- .../io_helidon_http_media_MediaContext.adoc | 8 +- .../io_helidon_integrations_neo4j_Neo4j.adoc | 6 +- ...grations_oci_ConfigFileStrategyConfig.adoc | 54 ++++++++++ ...integrations_oci_ConfigStrategyConfig.adoc | 83 ++++++++++++++ ...io_helidon_integrations_oci_OciConfig.adoc | 99 +++++++++++++++++ ...rations_oci_metrics_OciMetricsSupport.adoc | 10 +- ...ntegrations_oci_sdk_runtime_OciConfig.adoc | 68 ++++++------ ...ertificates_OciCertificatesTlsManager.adoc | 76 +++++++++++++ ...don_integrations_openapi_ui_OpenApiUi.adoc | 8 +- ..._api_ComponentMetricsSettings_Builder.adoc | 55 ++++++++++ ..._KeyPerformanceIndicatorMetricsConfig.adoc | 6 +- .../io_helidon_metrics_api_MetricsConfig.adoc | 24 ++--- .../io_helidon_metrics_api_ScopeConfig.adoc | 10 +- .../io_helidon_metrics_api_ScopingConfig.adoc | 10 +- ...rofile_openapi_MpOpenApiManagerConfig.adoc | 4 +- .../io_helidon_openapi_OpenApiFeature.adoc | 33 ++++-- .../config/io_helidon_scheduling_Cron.adoc | 6 +- .../io_helidon_scheduling_FixedRate.adoc | 32 +++--- .../config/io_helidon_security_Security.adoc | 6 +- .../io_helidon_security_SecurityTime.adoc | 16 +-- ...urity_providers_common_EvictableCache.adoc | 4 +- ...ders_google_login_GoogleTokenProvider.adoc | 4 +- ...ty_providers_header_HeaderAtnProvider.adoc | 4 +- ...viders_httpauth_HttpBasicAuthProvider.adoc | 4 +- ...y_providers_httpsign_HttpSignProvider.adoc | 14 +-- ...ders_httpsign_InboundClientDefinition.adoc | 6 +- ..._idcs_mapper_IdcsMtRoleMapperProvider.adoc | 14 +-- ...rs_idcs_mapper_IdcsRoleMapperProvider.adoc | 10 +- ...er_IdcsRoleMapperProviderBase_Builder.adoc | 10 +- ...on_security_providers_jwt_JwtProvider.adoc | 4 +- ..._security_providers_oidc_OidcProvider.adoc | 64 ++++++----- ...ity_providers_oidc_common_BaseBuilder.adoc | 20 ++-- ...rity_providers_oidc_common_OidcConfig.adoc | 64 ++++++----- ...ty_providers_oidc_common_TenantConfig.adoc | 20 ++-- ..._providers_zipkin_ZipkinTracerBuilder.adoc | 4 +- ...elidon_webclient_api_HttpClientConfig.adoc | 54 +++++----- ..._helidon_webclient_api_HttpConfigBase.adoc | 20 ++-- .../io_helidon_webclient_api_WebClient.adoc | 56 +++++----- ..._webclient_api_WebClientCookieManager.adoc | 8 +- .../io_helidon_webclient_grpc_GrpcClient.adoc | 51 +++++++++ ...bclient_grpc_GrpcClientProtocolConfig.adoc | 71 ++++++++++++ ...lient_http1_Http1ClientProtocolConfig.adoc | 12 +-- ...lient_http2_Http2ClientProtocolConfig.adoc | 18 ++-- ..._helidon_webclient_websocket_WsClient.adoc | 4 +- ...io_helidon_webserver_ConnectionConfig.adoc | 26 ++--- .../io_helidon_webserver_ListenerConfig.adoc | 51 ++++----- .../io_helidon_webserver_WebServer.adoc | 60 ++++++----- ...n_webserver_accesslog_AccessLogConfig.adoc | 24 +++-- ..._webserver_accesslog_AccessLogFeature.adoc | 24 +++-- ...idon_webserver_context_ContextFeature.adoc | 8 +- .../io_helidon_webserver_cors_CorsConfig.adoc | 22 ++-- ...io_helidon_webserver_cors_CorsFeature.adoc | 22 ++-- ...o_helidon_webserver_http1_Http1Config.adoc | 20 ++-- ...o_helidon_webserver_http2_Http2Config.adoc | 39 +++++-- ...idon_webserver_observe_ObserveFeature.adoc | 22 ++-- ..._webserver_observe_ObserverConfigBase.adoc | 4 +- ...bserver_observe_config_ConfigObserver.adoc | 6 +- ...bserver_observe_health_HealthObserver.adoc | 14 +-- ...n_webserver_observe_info_InfoObserver.adoc | 4 +- ...don_webserver_observe_log_LogObserver.adoc | 6 +- ...webserver_observe_log_LogStreamConfig.adoc | 12 +-- ...erver_observe_metrics_MetricsObserver.adoc | 24 ++--- ...erver_observe_tracing_TracingObserver.adoc | 28 +++-- ...elidon_webserver_security_PathsConfig.adoc | 30 +++--- ...on_webserver_security_SecurityFeature.adoc | 14 +-- ...on_webserver_security_SecurityHandler.adoc | 30 +++--- ...icecommon_RestServiceSettings_Builder.adoc | 52 +++++++++ ..._helidon_webserver_websocket_WsConfig.adoc | 10 +- .../config/io_opentracing_Tracer.adoc | 4 +- 94 files changed, 1537 insertions(+), 769 deletions(-) create mode 100644 config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/Javadoc.java create mode 100644 docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigFileStrategyConfig.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigStrategyConfig.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_integrations_oci_OciConfig.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_integrations_oci_tls_certificates_OciCertificatesTlsManager.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_metrics_api_ComponentMetricsSettings_Builder.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClient.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClientProtocolConfig.adoc create mode 100644 docs/src/main/asciidoc/config/io_helidon_webserver_servicecommon_RestServiceSettings_Builder.adoc diff --git a/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/Javadoc.java b/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/Javadoc.java new file mode 100644 index 00000000000..e8870da444a --- /dev/null +++ b/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/Javadoc.java @@ -0,0 +1,101 @@ +/* + * 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.metadata.processor; + +import java.util.regex.Pattern; + +import static io.helidon.common.processor.GeneratorTools.capitalize; + +/* +Possible improvements: +- @link - create a proper javadoc reference (i.e. always fully qualified reference), such as: + {@link:io.helidon.common.Type#method(java.lang.String)}, so we can generate a nice reference for docs +- @value - if possible, find the actual value (string, int etc.) and add it as `thevalue` +- @see - create a proper javadoc reference (as for @link) + */ +final class Javadoc { + private static final Pattern JAVADOC_CODE = Pattern.compile("\\{@code (.*?)}"); + private static final Pattern JAVADOC_LINK = Pattern.compile("\\{@link (.*?)}"); + private static final Pattern JAVADOC_VALUE = Pattern.compile("\\{@value (.*?)}"); + private static final Pattern JAVADOC_SEE = Pattern.compile("\\{@see (.*?)}"); + + private Javadoc() { + } + + /** + * Parses a Javadoc comment (provided as a string) into text that can be used for display/docs of the configuration option. + *

    + * The following steps are done: + *

      + *
    • {@code @param} is stripped from the text
    • + *
    • Any {@code @code} section: the code tag is removed, and surrounded with {@code '}
    • + *
    • Any {@code @link} section: the link tag is removed
    • + *
    • Any {@code @value} section: the value tag is removed, {code #} is replaced with {@code .}
    • + *
    • Any {@code @see} section: the see tag is removed, prefixed with {@code See}, + * {code #} is replaced with {@code .}
    • + *
    • {@code @return} is stripped from the text, and the first letter is capitalized
    • + *
    + * + * @param docComment "raw" javadoc from the source code + * @return description of the option + */ + static String parse(String docComment) { + if (docComment == null) { + return ""; + } + + String javadoc = docComment; + int index = javadoc.indexOf("@param"); + if (index > -1) { + javadoc = docComment.substring(0, index); + } + // replace all {@code xxx} with 'xxx' + javadoc = JAVADOC_CODE.matcher(javadoc).replaceAll(it -> javadocCode(it.group(1))); + // replace all {@link ...} with just the name + javadoc = JAVADOC_LINK.matcher(javadoc).replaceAll(it -> javadocLink(it.group(1))); + // replace all {@value ...} with just the reference + javadoc = JAVADOC_VALUE.matcher(javadoc).replaceAll(it -> javadocValue(it.group(1))); + // replace all {@see ...} with just the reference + javadoc = JAVADOC_SEE.matcher(javadoc).replaceAll(it -> javadocSee(it.group(1))); + + index = javadoc.indexOf("@return"); + if (index > -1) { + javadoc = javadoc.substring(0, index) + capitalize(javadoc.substring(index + 8)); + } + + return javadoc.trim(); + } + + private static String javadocSee(String originalValue) { + return "See " + javadocValue(originalValue); + } + + private static String javadocCode(String originalValue) { + return '`' + originalValue + '`'; + } + + private static String javadocLink(String originalValue) { + return javadocValue(originalValue); + } + + private static String javadocValue(String originalValue) { + if (originalValue.startsWith("#")) { + return originalValue.substring(1); + } + return originalValue.replace('#', '.'); + } +} diff --git a/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBase.java b/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBase.java index 431323ce5e8..c9ba18cd82f 100644 --- a/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBase.java +++ b/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBase.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,7 +19,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Predicate; -import java.util.regex.Pattern; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; @@ -38,8 +37,7 @@ abstract class TypeHandlerBase { static final String UNCONFIGURED_OPTION = "io.helidon.config.metadata.ConfiguredOption.UNCONFIGURED"; - private static final Pattern JAVADOC_CODE = Pattern.compile("\\{@code (.*?)}"); - private static final Pattern JAVADOC_LINK = Pattern.compile("\\{@link (.*?)}"); + private final ProcessingEnvironment aptEnv; TypeHandlerBase(ProcessingEnvironment aptEnv) { @@ -95,21 +93,7 @@ static String toConfigKey(String methodName) { } static String javadoc(String docComment) { - if (docComment == null) { - return ""; - } - - String javadoc = docComment; - int index = javadoc.indexOf("@param"); - if (index > -1) { - javadoc = docComment.substring(0, index); - } - // replace all {@code xxx} with 'xxx' - javadoc = JAVADOC_CODE.matcher(javadoc).replaceAll(it -> '`' + it.group(1) + '`'); - // replace all {@link ...} with just the name - javadoc = JAVADOC_LINK.matcher(javadoc).replaceAll(it -> it.group(1)); - - return javadoc.trim(); + return Javadoc.parse(docComment); } String key(TypedElementInfo elementInfo, ConfiguredOptionData configuredOption) { @@ -197,15 +181,43 @@ void addSuperClasses(ConfiguredType type, TypeInfo typeInfo, TypeName requiredAn } } + /* + If the type is an enum that is accessible to us, provide its element, otherwise empty + */ + Optional toEnum(TypeName type) { + TypeElement typeElement = aptElements().getTypeElement(type.fqName()); + if (typeElement == null) { + return Optional.empty(); + } + if (typeElement.getKind() != ElementKind.ENUM) { + return Optional.empty(); + } + + return Optional.of(typeElement); + } + + List allowedValuesEnum(ConfiguredOptionData data, TypeElement typeElement) { + if (data.allowedValues().isEmpty()) { + // this was already processed due to an explicit type defined in the annotation + // or allowed values explicitly configured in annotation + return data.allowedValues(); + } + return allowedValuesEnum(typeElement); + } + + private List allowedValuesEnum(TypeElement typeElement) { + return typeElement.getEnclosedElements() + .stream() + .filter(element -> element.getKind().equals(ElementKind.ENUM_CONSTANT)) + .map(element -> new ConfiguredOptionData.AllowedValue(element.toString(), + javadoc(aptElements().getDocComment(element)))) + .toList(); + } + private List allowedValues(TypeName type) { TypeElement typeElement = aptElements().getTypeElement(type.fqName()); if (typeElement != null && typeElement.getKind() == ElementKind.ENUM) { - return typeElement.getEnclosedElements() - .stream() - .filter(element -> element.getKind().equals(ElementKind.ENUM_CONSTANT)) - .map(element -> new ConfiguredOptionData.AllowedValue(element.toString(), - javadoc(aptElements().getDocComment(element)))) - .toList(); + return allowedValuesEnum(typeElement); } return List.of(); } diff --git a/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBuilderApi.java b/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBuilderApi.java index ac1bfb8f45a..ab805f89e15 100644 --- a/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBuilderApi.java +++ b/config/metadata-processor/src/main/java/io/helidon/config/metadata/processor/TypeHandlerBuilderApi.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,8 +17,10 @@ package io.helidon.config.metadata.processor; import java.util.List; +import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.TypeElement; import io.helidon.common.types.TypeInfo; import io.helidon.common.types.TypeName; @@ -97,7 +99,16 @@ private void processBlueprintMethod(TypeName typeName, ConfiguredType configured OptionType type = BlueprintUtil.typeForBlueprintFromSignature(aptMessager(), aptElements(), elementInfo, data); boolean optional = defaultValue != null || data.optional(); boolean deprecated = data.deprecated(); - List allowedValues = allowedValues(data, type.elementType()); + + List allowedValues; + Optional anEnum = toEnum(type.elementType()); + if (anEnum.isPresent() && defaultValue != null) { + // prefix the default value with the enum name to make it more readable + defaultValue = type.elementType().className() + "." + defaultValue; + allowedValues = allowedValuesEnum(data, anEnum.get()); + } else { + allowedValues = allowedValues(data, type.elementType()); + } List paramTypes = List.of(elementInfo.typeName()); diff --git a/docs/src/main/asciidoc/config/config_reference.adoc b/docs/src/main/asciidoc/config/config_reference.adoc index af085155c9e..b94bb7436d4 100644 --- a/docs/src/main/asciidoc/config/config_reference.adoc +++ b/docs/src/main/asciidoc/config/config_reference.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -30,21 +30,28 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_common_configurable_AllowList.adoc[AllowList (common.configurable)] - xref:{rootdir}/config/io_helidon_faulttolerance_Async.adoc[Async (faulttolerance)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_common_BaseBuilder.adoc[BaseBuilder (security.providers.oidc.common)] +- xref:{rootdir}/config/io_helidon_metrics_api_ComponentMetricsSettings_Builder.adoc[Builder (metrics.api.ComponentMetricsSettings)] - xref:{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProviderBase_Builder.adoc[Builder (security.providers.idcs.mapper.IdcsRoleMapperProviderBase)] - xref:{rootdir}/config/io_helidon_webserver_servicecommon_HelidonFeatureSupport_Builder.adoc[Builder (webserver.servicecommon.HelidonFeatureSupport)] +- xref:{rootdir}/config/io_helidon_webserver_servicecommon_RestServiceSettings_Builder.adoc[Builder (webserver.servicecommon.RestServiceSettings)] - xref:{rootdir}/config/io_helidon_faulttolerance_Bulkhead.adoc[Bulkhead (faulttolerance)] - xref:{rootdir}/config/io_helidon_faulttolerance_CircuitBreaker.adoc[CircuitBreaker (faulttolerance)] -- xref:{rootdir}/config/io_helidon_metrics_api_ComponentMetricsSettings.adoc[ComponentMetricsSettings (metrics.api)] +- xref:{rootdir}/config/io_helidon_integrations_oci_ConfigFileStrategyConfig.adoc[ConfigFileStrategyConfig (integrations.oci)] - xref:{rootdir}/config/io_helidon_webserver_observe_config_ConfigObserver.adoc[ConfigObserver (webserver.observe.config)] +- xref:{rootdir}/config/io_helidon_integrations_oci_ConfigStrategyConfig.adoc[ConfigStrategyConfig (integrations.oci)] - xref:{rootdir}/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser (security.providers.httpauth.ConfigUserStore)] - xref:{rootdir}/config/io_helidon_webserver_ConnectionConfig.adoc[ConnectionConfig (webserver)] - xref:{rootdir}/config/io_helidon_http_encoding_ContentEncodingContext.adoc[ContentEncodingContext (http.encoding)] - xref:{rootdir}/config/io_helidon_webserver_context_ContextFeature.adoc[ContextFeature (webserver.context)] - xref:{rootdir}/config/io_helidon_webserver_cors_CorsConfig.adoc[CorsConfig (webserver.cors)] - xref:{rootdir}/config/io_helidon_webserver_cors_CorsFeature.adoc[CorsFeature (webserver.cors)] +- xref:{rootdir}/config/io_helidon_scheduling_Cron.adoc[Cron (scheduling)] - xref:{rootdir}/config/io_helidon_cors_CrossOriginConfig.adoc[CrossOriginConfig (cors)] - xref:{rootdir}/config/io_helidon_security_providers_common_EvictableCache.adoc[EvictableCache (security.providers.common)] +- xref:{rootdir}/config/io_helidon_scheduling_FixedRate.adoc[FixedRate (scheduling)] - xref:{rootdir}/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc[GoogleTokenProvider (security.providers.google.login)] +- xref:{rootdir}/config/io_helidon_webclient_grpc_GrpcClient.adoc[GrpcClient (webclient.grpc)] +- xref:{rootdir}/config/io_helidon_webclient_grpc_GrpcClientProtocolConfig.adoc[GrpcClientProtocolConfig (webclient.grpc)] - xref:{rootdir}/config/io_helidon_webserver_grpc_GrpcConfig.adoc[GrpcConfig (webserver.grpc)] - xref:{rootdir}/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc[HeaderAtnProvider (security.providers.header)] - xref:{rootdir}/config/io_helidon_security_providers_httpsign_SignedHeadersConfig_HeadersConfig.adoc[HeadersConfig (security.providers.httpsign.SignedHeadersConfig)] @@ -78,8 +85,11 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_integrations_micrometer_MicrometerFeature.adoc[MicrometerFeature (integrations.micrometer)] - xref:{rootdir}/config/io_helidon_config_mp_MpConfigBuilder.adoc[MpConfigBuilder (config.mp)] - xref:{rootdir}/config/io_helidon_microprofile_openapi_MpOpenApiManagerConfig.adoc[MpOpenApiManagerConfig (microprofile.openapi)] +- xref:{rootdir}/config/io_helidon_integrations_neo4j_Neo4j.adoc[Neo4j (integrations.neo4j)] - xref:{rootdir}/config/io_helidon_webserver_observe_ObserveFeature.adoc[ObserveFeature (webserver.observe)] - xref:{rootdir}/config/io_helidon_webserver_observe_ObserverConfigBase.adoc[ObserverConfigBase (webserver.observe)] +- xref:{rootdir}/config/io_helidon_integrations_oci_tls_certificates_OciCertificatesTlsManager.adoc[OciCertificatesTlsManager (integrations.oci.tls.certificates)] +- xref:{rootdir}/config/io_helidon_integrations_oci_OciConfig.adoc[OciConfig (integrations.oci)] - xref:{rootdir}/config/io_helidon_integrations_oci_sdk_runtime_OciConfig.adoc[OciConfig (integrations.oci.sdk.runtime)] - xref:{rootdir}/config/io_helidon_integrations_oci_metrics_OciMetricsSupport.adoc[OciMetricsSupport (integrations.oci.metrics)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig (security.providers.oidc.common)] @@ -94,8 +104,8 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_webclient_api_Proxy.adoc[Proxy (webclient.api)] - xref:{rootdir}/config/io_helidon_http_RequestedUriDiscoveryContext.adoc[RequestedUriDiscoveryContext (http)] - xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource (common.configurable)] -- xref:{rootdir}/config/io_helidon_webserver_servicecommon_RestServiceSettings.adoc[RestServiceSettings (webserver.servicecommon)] - xref:{rootdir}/config/io_helidon_faulttolerance_Retry.adoc[Retry (faulttolerance)] +- xref:{rootdir}/config/io_helidon_common_tls_RevocationConfig.adoc[RevocationConfig (common.tls)] - xref:{rootdir}/config/io_helidon_common_configurable_ScheduledThreadPoolConfig.adoc[ScheduledThreadPoolConfig (common.configurable)] - xref:{rootdir}/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc[ScheduledThreadPoolSupplier (common.configurable)] - xref:{rootdir}/config/io_helidon_metrics_api_ScopeConfig.adoc[ScopeConfig (metrics.api)] @@ -107,6 +117,7 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_microprofile_server_Server.adoc[Server (microprofile.server)] - xref:{rootdir}/config/io_helidon_common_socket_SocketOptions.adoc[SocketOptions (common.socket)] - xref:{rootdir}/config/io_helidon_metrics_api_Tag.adoc[Tag (metrics.api)] +- xref:{rootdir}/config/io_helidon_scheduling_TaskConfig.adoc[TaskConfig (scheduling)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_common_TenantConfig.adoc[TenantConfig (security.providers.oidc.common)] - xref:{rootdir}/config/io_helidon_common_configurable_ThreadPoolConfig.adoc[ThreadPoolConfig (common.configurable)] - xref:{rootdir}/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc[ThreadPoolSupplier (common.configurable)] @@ -124,8 +135,4 @@ The following section lists all configurable types in Helidon. - xref:{rootdir}/config/io_helidon_webserver_websocket_WsConfig.adoc[WsConfig (webserver.websocket)] - xref:{rootdir}/config/io_helidon_tracing_providers_zipkin_ZipkinTracerBuilder.adoc[ZipkinTracerBuilder (tracing.providers.zipkin)] - xref:{rootdir}/config/io_opentracing_Tracer.adoc[io_opentracing_Tracer] -- xref:{rootdir}/config/org_eclipse_microprofile_config_Config.adoc[org_eclipse_microprofile_config_Config] -- xref:{rootdir}/config/io_helidon_scheduling_Cron.adoc[Cron (scheduling)] -- xref:{rootdir}/config/io_helidon_scheduling_FixedRate.adoc[FixedRate (scheduling)] -- xref:{rootdir}/config/io_helidon_scheduling_TaskConfig.adoc[TaskConfig (scheduling)] - +- xref:{rootdir}/config/org_eclipse_microprofile_config_Config.adoc[org_eclipse_microprofile_config_Config] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_AllowList.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_AllowList.adoc index c16afe47238..733341db9e0 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_AllowList.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_AllowList.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,31 +46,31 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c not deny all strings but rather represents the absence of a universal match, meaning that other allow and deny settings determine the matching outcomes. - @return whether to allow all strings to match (subject to "deny" conditions) + Whether to allow all strings to match (subject to "deny" conditions) |`allow.exact` |string[] |{nbsp} |Exact strings to allow. - @return exact strings to allow + Exact strings to allow |`allow.pattern` |Pattern[] |{nbsp} |Patterns specifying strings to allow. - @return patterns which allow matching + Patterns which allow matching |`allow.prefix` |string[] |{nbsp} |Prefixes specifying strings to allow. - @return prefixes which allow matching + Prefixes which allow matching |`allow.suffix` |string[] |{nbsp} |Suffixes specifying strings to allow. - @return suffixes which allow matching + Suffixes which allow matching |`deny.exact` |string[] |{nbsp} |Exact strings to deny. - @return exact strings to allow + Exact strings to allow |`deny.pattern` |Pattern[] |{nbsp} |Patterns specifying strings to deny. - @return patterns which deny matching + Patterns which deny matching |`deny.prefix` |string[] |{nbsp} |Prefixes specifying strings to deny. - @return prefixes which deny matching + Prefixes which deny matching |`deny.suffix` |string[] |{nbsp} |Suffixes specifying strings to deny. - @return suffixes which deny matching + Suffixes which deny matching |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_LruCache.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_LruCache.adoc index 46c4041d44f..c06372ef3cc 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_LruCache.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_LruCache.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -42,9 +42,9 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c |=== |key |type |default value |description -|`capacity` |int |`10000` |Configure capacity of the cache. Defaults to `LruCache#DEFAULT_CAPACITY`. +|`capacity` |int |`10000` |Configure capacity of the cache. Defaults to LruCache.DEFAULT_CAPACITY. - @return maximal number of records in the cache before the oldest one is removed + Maximal number of records in the cache before the oldest one is removed |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_Resource.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_Resource.adoc index a2f19a36dbd..4745e53b8c0 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_Resource.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_Resource.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,34 +44,34 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c |`content` |string |{nbsp} |Binary content of the resource (base64 encoded). - @return binary content + Binary content |`content-plain` |string |{nbsp} |Plain content of the resource (text). - @return plain content + Plain content |`description` |string |{nbsp} |Description of this resource when configured through plain text or binary. - @return description + Description |`path` |Path |{nbsp} |Resource is located on filesystem. - @return path of the resource + Path of the resource |`proxy-host` |string |{nbsp} |Host of the proxy when using URI. - @return proxy host + Proxy host |`proxy-port` |int |`80` |Port of the proxy when using URI. - @return proxy port + Proxy port |`resource-path` |string |{nbsp} |Resource is located on classpath. - @return classpath location of the resource + Classpath location of the resource |`uri` |URI |{nbsp} |Resource is available on a java.net.URI. - @return of the resource + Of the resource @see #proxy() @see #useProxy() |`use-proxy` |boolean |`true` |Whether to use proxy. If set to `false`, proxy will not be used even if configured. When set to `true` (default), proxy will be used if configured. - @return whether to use proxy if configured + Whether to use proxy if configured |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolConfig.adoc index f69a5c31c85..8e8a4a02a3d 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,26 +43,26 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c |key |type |default value |description |`core-pool-size` |int |`16` |Core pool size of the thread pool executor. - Defaults to `DEFAULT_CORE_POOL_SIZE`. + Defaults to DEFAULT_CORE_POOL_SIZE. - @return corePoolSize see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize() + CorePoolSize see java.util.concurrent.ThreadPoolExecutor.getCorePoolSize() |`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. - Defaults to `DEFAULT_IS_DAEMON`. + Defaults to DEFAULT_IS_DAEMON. - @return whether the threads are daemon threads + Whether the threads are daemon threads |`prestart` |boolean |`false` |Whether to prestart core threads in this thread pool executor. - Defaults to `DEFAULT_PRESTART`. + Defaults to DEFAULT_PRESTART. - @return whether to prestart the threads + Whether to prestart the threads |`thread-name-prefix` |string |`helidon-` |Name prefix for threads in this thread pool executor. - Defaults to `DEFAULT_THREAD_NAME_PREFIX`. + Defaults to DEFAULT_THREAD_NAME_PREFIX. - @return prefix of a thread name + Prefix of a thread name |`virtual-threads` |boolean |{nbsp} |When configured to `true`, an unbounded virtual executor service (project Loom) will be used. If enabled, all other configuration options of this executor service are ignored! - @return whether to use virtual threads or not, defaults to `false` + Whether to use virtual threads or not, defaults to `false` |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc index f69a5c31c85..8e8a4a02a3d 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ScheduledThreadPoolSupplier.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,26 +43,26 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c |key |type |default value |description |`core-pool-size` |int |`16` |Core pool size of the thread pool executor. - Defaults to `DEFAULT_CORE_POOL_SIZE`. + Defaults to DEFAULT_CORE_POOL_SIZE. - @return corePoolSize see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize() + CorePoolSize see java.util.concurrent.ThreadPoolExecutor.getCorePoolSize() |`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. - Defaults to `DEFAULT_IS_DAEMON`. + Defaults to DEFAULT_IS_DAEMON. - @return whether the threads are daemon threads + Whether the threads are daemon threads |`prestart` |boolean |`false` |Whether to prestart core threads in this thread pool executor. - Defaults to `DEFAULT_PRESTART`. + Defaults to DEFAULT_PRESTART. - @return whether to prestart the threads + Whether to prestart the threads |`thread-name-prefix` |string |`helidon-` |Name prefix for threads in this thread pool executor. - Defaults to `DEFAULT_THREAD_NAME_PREFIX`. + Defaults to DEFAULT_THREAD_NAME_PREFIX. - @return prefix of a thread name + Prefix of a thread name |`virtual-threads` |boolean |{nbsp} |When configured to `true`, an unbounded virtual executor service (project Loom) will be used. If enabled, all other configuration options of this executor service are ignored! - @return whether to use virtual threads or not, defaults to `false` + Whether to use virtual threads or not, defaults to `false` |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolConfig.adoc index 297c8c57ed9..0470d14e859 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,9 +43,9 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c |key |type |default value |description |`core-pool-size` |int |`10` |Core pool size of the thread pool executor. - Defaults to `DEFAULT_CORE_POOL_SIZE`. + Defaults to DEFAULT_CORE_POOL_SIZE. - @return corePoolSize see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize() + CorePoolSize see java.util.concurrent.ThreadPoolExecutor.getCorePoolSize() |`growth-rate` |int |`0` |The percentage of task submissions that should result in adding threads, expressed as a value from 1 to 100. The rate applies only when all of the following are true: @@ -56,45 +56,45 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c For example, a rate of 20 means that while these conditions are met one thread will be added for every 5 submitted tasks. - Defaults to `DEFAULT_GROWTH_RATE` + Defaults to DEFAULT_GROWTH_RATE - @return the growth rate + The growth rate |`growth-threshold` |int |`1000` |The queue size above which pool growth will be considered if the pool is not fixed size. - Defaults to `DEFAULT_GROWTH_THRESHOLD`. + Defaults to DEFAULT_GROWTH_THRESHOLD. - @return the growth threshold + The growth threshold |`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. - Defaults to `DEFAULT_IS_DAEMON`. + Defaults to DEFAULT_IS_DAEMON. - @return whether the threads are daemon threads + Whether the threads are daemon threads |`keep-alive` |Duration |`PT3M` |Keep alive of the thread pool executor. - Defaults to `DEFAULT_KEEP_ALIVE`. + Defaults to DEFAULT_KEEP_ALIVE. - @return keep alive see java.util.concurrent.ThreadPoolExecutor#getKeepAliveTime(java.util.concurrent.TimeUnit) + Keep alive see java.util.concurrent.ThreadPoolExecutor.getKeepAliveTime(java.util.concurrent.TimeUnit) |`max-pool-size` |int |`50` |Max pool size of the thread pool executor. - Defaults to `DEFAULT_MAX_POOL_SIZE`. + Defaults to DEFAULT_MAX_POOL_SIZE. - @return maxPoolSize see java.util.concurrent.ThreadPoolExecutor#getMaximumPoolSize() + MaxPoolSize see java.util.concurrent.ThreadPoolExecutor.getMaximumPoolSize() |`name` |string |{nbsp} |Name of this thread pool executor. - @return the pool name + The pool name |`queue-capacity` |int |`10000` |Queue capacity of the thread pool executor. - Defaults to `DEFAULT_QUEUE_CAPACITY`. + Defaults to DEFAULT_QUEUE_CAPACITY. - @return capacity of the queue backing the executor + Capacity of the queue backing the executor |`should-prestart` |boolean |`true` |Whether to prestart core threads in this thread pool executor. - Defaults to `DEFAULT_PRESTART`. + Defaults to DEFAULT_PRESTART. - @return whether to prestart the threads + Whether to prestart the threads |`thread-name-prefix` |string |{nbsp} |Name prefix for threads in this thread pool executor. - Defaults to `DEFAULT_THREAD_NAME_PREFIX`. + Defaults to DEFAULT_THREAD_NAME_PREFIX. - @return prefix of a thread name + Prefix of a thread name |`virtual-threads` |boolean |{nbsp} |When configured to `true`, an unbounded virtual executor service (project Loom) will be used. If enabled, all other configuration options of this executor service are ignored! - @return whether to use virtual threads or not, defaults to `false` + Whether to use virtual threads or not, defaults to `false` |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc index 297c8c57ed9..0470d14e859 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_configurable_ThreadPoolSupplier.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,9 +43,9 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c |key |type |default value |description |`core-pool-size` |int |`10` |Core pool size of the thread pool executor. - Defaults to `DEFAULT_CORE_POOL_SIZE`. + Defaults to DEFAULT_CORE_POOL_SIZE. - @return corePoolSize see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize() + CorePoolSize see java.util.concurrent.ThreadPoolExecutor.getCorePoolSize() |`growth-rate` |int |`0` |The percentage of task submissions that should result in adding threads, expressed as a value from 1 to 100. The rate applies only when all of the following are true: @@ -56,45 +56,45 @@ Type: link:{javadoc-base-url}/io.helidon.common.configurable/io/helidon/common/c For example, a rate of 20 means that while these conditions are met one thread will be added for every 5 submitted tasks. - Defaults to `DEFAULT_GROWTH_RATE` + Defaults to DEFAULT_GROWTH_RATE - @return the growth rate + The growth rate |`growth-threshold` |int |`1000` |The queue size above which pool growth will be considered if the pool is not fixed size. - Defaults to `DEFAULT_GROWTH_THRESHOLD`. + Defaults to DEFAULT_GROWTH_THRESHOLD. - @return the growth threshold + The growth threshold |`is-daemon` |boolean |`true` |Is daemon of the thread pool executor. - Defaults to `DEFAULT_IS_DAEMON`. + Defaults to DEFAULT_IS_DAEMON. - @return whether the threads are daemon threads + Whether the threads are daemon threads |`keep-alive` |Duration |`PT3M` |Keep alive of the thread pool executor. - Defaults to `DEFAULT_KEEP_ALIVE`. + Defaults to DEFAULT_KEEP_ALIVE. - @return keep alive see java.util.concurrent.ThreadPoolExecutor#getKeepAliveTime(java.util.concurrent.TimeUnit) + Keep alive see java.util.concurrent.ThreadPoolExecutor.getKeepAliveTime(java.util.concurrent.TimeUnit) |`max-pool-size` |int |`50` |Max pool size of the thread pool executor. - Defaults to `DEFAULT_MAX_POOL_SIZE`. + Defaults to DEFAULT_MAX_POOL_SIZE. - @return maxPoolSize see java.util.concurrent.ThreadPoolExecutor#getMaximumPoolSize() + MaxPoolSize see java.util.concurrent.ThreadPoolExecutor.getMaximumPoolSize() |`name` |string |{nbsp} |Name of this thread pool executor. - @return the pool name + The pool name |`queue-capacity` |int |`10000` |Queue capacity of the thread pool executor. - Defaults to `DEFAULT_QUEUE_CAPACITY`. + Defaults to DEFAULT_QUEUE_CAPACITY. - @return capacity of the queue backing the executor + Capacity of the queue backing the executor |`should-prestart` |boolean |`true` |Whether to prestart core threads in this thread pool executor. - Defaults to `DEFAULT_PRESTART`. + Defaults to DEFAULT_PRESTART. - @return whether to prestart the threads + Whether to prestart the threads |`thread-name-prefix` |string |{nbsp} |Name prefix for threads in this thread pool executor. - Defaults to `DEFAULT_THREAD_NAME_PREFIX`. + Defaults to DEFAULT_THREAD_NAME_PREFIX. - @return prefix of a thread name + Prefix of a thread name |`virtual-threads` |boolean |{nbsp} |When configured to `true`, an unbounded virtual executor service (project Loom) will be used. If enabled, all other configuration options of this executor service are ignored! - @return whether to use virtual threads or not, defaults to `false` + Whether to use virtual threads or not, defaults to `false` |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_pki_Keys.adoc b/docs/src/main/asciidoc/config/io_helidon_common_pki_Keys.adoc index aff9e30addd..73ac006f60d 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_pki_Keys.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_pki_Keys.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,14 +44,14 @@ Type: link:{javadoc-base-url}/io.helidon.common.pki/io/helidon/common/pki/Keys.h |`keystore` |xref:{rootdir}/config/io_helidon_common_pki_KeystoreKeys.adoc[KeystoreKeys] |{nbsp} |Configure keys from a keystore. Once the config object is built, this option will ALWAYS be empty. All keys from the keystore will be - populated to #privateKey(), #publicKey(), #publicCert() etc. + populated to privateKey(), publicKey(), publicCert() etc. - @return keystore configuration + Keystore configuration |`pem` |xref:{rootdir}/config/io_helidon_common_pki_PemKeys.adoc[PemKeys] |{nbsp} |Configure keys from pem file(s). Once the config object is built, this option will ALWAYS be empty. All keys from the keystore will be - populated to #privateKey(), #publicKey(), #publicCert() etc. + populated to privateKey(), publicKey(), publicCert() etc. - @return pem based definition + Pem based definition |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_pki_KeystoreKeys.adoc b/docs/src/main/asciidoc/config/io_helidon_common_pki_KeystoreKeys.adoc index fa9061ff340..879150c5ae8 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_pki_KeystoreKeys.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_pki_KeystoreKeys.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -41,7 +41,7 @@ Type: link:{javadoc-base-url}/io.helidon.common.pki/io/helidon/common/pki/Keysto |`resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Keystore resource definition. - @return keystore resource, from file path, classpath, URL etc. + Keystore resource, from file path, classpath, URL etc. |=== @@ -55,31 +55,31 @@ Type: link:{javadoc-base-url}/io.helidon.common.pki/io/helidon/common/pki/Keysto |`cert-chain.alias` |string |{nbsp} |Alias of an X.509 chain. - @return alias of certificate chain in the keystore + Alias of certificate chain in the keystore |`cert.alias` |string |{nbsp} |Alias of X.509 certificate of public key. Used to load both the certificate and public key. - @return alias under which the certificate is stored in the keystore + Alias under which the certificate is stored in the keystore |`key.alias` |string |{nbsp} |Alias of the private key in the keystore. - @return alias of the key in the keystore + Alias of the key in the keystore |`key.passphrase` |char[] |{nbsp} |Pass-phrase of the key in the keystore (used for private keys). This is (by default) the same as keystore passphrase - only configure if it differs from keystore passphrase. - @return pass-phrase of the key + Pass-phrase of the key |`passphrase` |char[] |{nbsp} |Pass-phrase of the keystore (supported with JKS and PKCS12 keystores). - @return keystore password to use + Keystore password to use |`trust-store` |boolean |`false` |If you want to build a trust store, call this method to add all certificates present in the keystore to certificate list. - @return whether this is a trust store + Whether this is a trust store |`type` |string |`PKCS12` |Set type of keystore. - Defaults to `DEFAULT_KEYSTORE_TYPE`, + Defaults to DEFAULT_KEYSTORE_TYPE, expected are other keystore types supported by java then can store keys under aliases. - @return keystore type to load the key + Keystore type to load the key |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_pki_PemKeys.adoc b/docs/src/main/asciidoc/config/io_helidon_common_pki_PemKeys.adoc index 2b166bedd75..0ce01213cc4 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_pki_PemKeys.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_pki_PemKeys.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,20 +44,20 @@ Type: link:{javadoc-base-url}/io.helidon.common.pki/io/helidon/common/pki/PemKey |`cert-chain.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Load certificate chain from PEM resource. - @return resource (e.g. classpath, file path, URL etc.) + Resource (e.g. classpath, file path, URL etc.) |`certificates.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Read one or more certificates in PEM format from a resource definition. Used eg: in a trust store. - @return key resource (file, classpath, URL etc.) + Key resource (file, classpath, URL etc.) |`key.passphrase` |char[] |{nbsp} |Passphrase for private key. If the key is encrypted (and in PEM PKCS#8 format), this passphrase will be used to decrypt it. - @return passphrase used to encrypt the private key + Passphrase used to encrypt the private key |`key.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Read a private key from PEM format from a resource definition. - @return key resource (file, classpath, URL etc.) + Key resource (file, classpath, URL etc.) |`public-key.resource` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |Read a public key from PEM format from a resource definition. - @return public key resource (file, classpath, URL etc.) + Public key resource (file, classpath, URL etc.) |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_socket_SocketOptions.adoc b/docs/src/main/asciidoc/config/io_helidon_common_socket_SocketOptions.adoc index 1dd75c14589..58cb21656f3 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_socket_SocketOptions.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_socket_SocketOptions.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,32 +44,32 @@ Type: link:{javadoc-base-url}/io.helidon.common.socket/io/helidon/common/socket/ |`connect-timeout` |Duration |`PT10S` |Socket connect timeout. Default is 10 seconds. - @return connect timeout duration + Connect timeout duration |`read-timeout` |Duration |`PT30S` |Socket read timeout. Default is 30 seconds. - @return read timeout duration + Read timeout duration |`socket-keep-alive` |boolean |`true` |Configure socket keep alive. Default is `true`. - @return keep alive + Keep alive @see java.net.StandardSocketOptions#SO_KEEPALIVE -|`socket-receive-buffer-size` |int |`32768` |Socket receive buffer size. +|`socket-receive-buffer-size` |int |{nbsp} |Socket receive buffer size. - @return buffer size, in bytes + Buffer size, in bytes @see java.net.StandardSocketOptions#SO_RCVBUF |`socket-reuse-address` |boolean |`true` |Socket reuse address. Default is `true`. - @return whether to reuse address + Whether to reuse address @see java.net.StandardSocketOptions#SO_REUSEADDR -|`socket-send-buffer-size` |int |`32768` |Socket send buffer size. +|`socket-send-buffer-size` |int |{nbsp} |Socket send buffer size. - @return buffer size, in bytes + Buffer size, in bytes @see java.net.StandardSocketOptions#SO_SNDBUF |`tcp-no-delay` |boolean |`false` |This option may improve performance on some systems. Default is `false`. - @return whether to use TCP_NODELAY, defaults to `false` + Whether to use TCP_NODELAY, defaults to `false` @see java.net.StandardSocketOptions#TCP_NODELAY |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_tls_RevocationConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_common_tls_RevocationConfig.adoc index 5f466801518..20c489ce277 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_tls_RevocationConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_tls_RevocationConfig.adoc @@ -45,23 +45,28 @@ Type: link:{javadoc-base-url}/io.helidon.common.tls/io/helidon/common/tls/Revoca |`check-only-end-entity` |boolean |`false` |Only check the revocation status of end-entity certificates. Default value is `false`. - @return whether to check only end-entity certificates + Whether to check only end-entity certificates |`enabled` |boolean |`false` |Flag indicating whether this revocation config is enabled. - @return enabled flag + Enabled flag |`fallback-enabled` |boolean |`true` |Enable fallback to the less preferred checking option. +
    + If the primary method for revocation checking fails to verify the revocation status of a certificate + (such as using a CRL or OCSP), the checker will attempt alternative methods. This option ensures + whether revocation checking is performed strictly according to the specified method, or should fallback + to the one less preferred. OCSP is preferred over the CRL by default. - @return whether to allow fallback to the less preferred checking option + Whether to allow fallback to the less preferred checking option |`ocsp-responder-uri` |URI |{nbsp} |The URI that identifies the location of the OCSP responder. This overrides the `ocsp.responderURL` security property and any responder specified in a certificate's Authority Information Access Extension, as defined in RFC 5280. - @return OCSP responder URI + OCSP responder URI |`prefer-crl-over-ocsp` |boolean |`false` |Prefer CRL over OCSP. - Default value is `false`. + Default value is `false`. OCSP is preferred over the CRL by default. - @return whether to prefer CRL over OCSP + Whether to prefer CRL over OCSP |`soft-fail-enabled` |boolean |`false` |Allow revocation check to succeed if the revocation status cannot be determined for one of the following reasons: @@ -71,7 +76,7 @@ Type: link:{javadoc-base-url}/io.helidon.common.tls/io/helidon/common/tls/Revoca - The OCSP responder returns one of the following errors specified in section 2.3 of RFC 2560: internalError or tryLater. -@return whether soft fail is enabled +Whether soft fail is enabled |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_common_tls_Tls.adoc b/docs/src/main/asciidoc/config/io_helidon_common_tls_Tls.adoc index b4af4a24d5e..2810eb15535 100644 --- a/docs/src/main/asciidoc/config/io_helidon_common_tls_Tls.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_common_tls_Tls.adoc @@ -44,75 +44,75 @@ Type: link:{javadoc-base-url}/io.helidon.common.tls/io/helidon/common/tls/Tls.ht |`cipher-suite` |string[] |{nbsp} |Enabled cipher suites for TLS communication. - @return cipher suits to enable, by default (or if list is empty), all available cipher suites + Cipher suits to enable, by default (or if list is empty), all available cipher suites are enabled -|`client-auth` |TlsClientAuth (REQUIRED, OPTIONAL, NONE) |`NONE` |Configure requirement for mutual TLS. +|`client-auth` |TlsClientAuth |`TlsClientAuth.NONE` |Configure requirement for mutual TLS. - @return what type of mutual TLS to use, defaults to TlsClientAuth#NONE + What type of mutual TLS to use, defaults to TlsClientAuth.NONE |`enabled` |boolean |`true` |Flag indicating whether Tls is enabled. - @return enabled flag + Enabled flag |`endpoint-identification-algorithm` |string |`HTTPS` |Identification algorithm for SSL endpoints. - @return configure endpoint identification algorithm, or set to `NONE` + Configure endpoint identification algorithm, or set to `NONE` to disable endpoint identification (equivalent to hostname verification). - Defaults to `Tls#ENDPOINT_IDENTIFICATION_HTTPS` + Defaults to Tls.ENDPOINT_IDENTIFICATION_HTTPS |`internal-keystore-provider` |string |{nbsp} |Provider of the key stores used internally to create a key and trust manager factories. - @return keystore provider, if not defined, provider is not specified + Keystore provider, if not defined, provider is not specified |`internal-keystore-type` |string |{nbsp} |Type of the key stores used internally to create a key and trust manager factories. - @return keystore type, defaults to java.security.KeyStore#getDefaultType() + Keystore type, defaults to java.security.KeyStore.getDefaultType() |`key-manager-factory-algorithm` |string |{nbsp} |Algorithm of the key manager factory used when private key is defined. - Defaults to javax.net.ssl.KeyManagerFactory#getDefaultAlgorithm(). + Defaults to javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm(). - @return algorithm to use + Algorithm to use |`manager` |io.helidon.common.tls.TlsManager (service provider interface) |{nbsp} |The Tls manager. If one is not explicitly defined in the config then a default manager will be created. - @return the tls manager of the tls instance + The tls manager of the tls instance @see ConfiguredTlsManager |`private-key` |PrivateKey |{nbsp} |Private key to use. For server side TLS, this is required. For client side TLS, this is optional (used when mutual TLS is enabled). - @return private key to use + Private key to use |`protocol` |string |`TLS` |Configure the protocol used to obtain an instance of javax.net.ssl.SSLContext. - @return protocol to use, defaults to `DEFAULT_PROTOCOL` + Protocol to use, defaults to DEFAULT_PROTOCOL |`protocols` |string[] |{nbsp} |Enabled protocols for TLS communication. Example of valid values for `TLS` protocol: `TLSv1.3`, `TLSv1.2` - @return protocols to enable, by default (or if list is empty), all available protocols are enabled + Protocols to enable, by default (or if list is empty), all available protocols are enabled |`provider` |string |{nbsp} |Use explicit provider to obtain an instance of javax.net.ssl.SSLContext. - @return provider to use, defaults to none (only #protocol() is used by default) + Provider to use, defaults to none (only protocol() is used by default) |`revocation` |xref:{rootdir}/config/io_helidon_common_tls_RevocationConfig.adoc[RevocationConfig] |{nbsp} |Certificate revocation check configuration. - @return certificate revocation configuration + Certificate revocation configuration |`secure-random-algorithm` |string |{nbsp} |Algorithm to use when creating a new secure random. - @return algorithm to use, by default uses java.security.SecureRandom constructor + Algorithm to use, by default uses java.security.SecureRandom constructor |`secure-random-provider` |string |{nbsp} |Provider to use when creating a new secure random. - When defined, #secureRandomAlgorithm() must be defined as well. + When defined, secureRandomAlgorithm() must be defined as well. - @return provider to use, by default no provider is specified -|`session-cache-size` |int |`1024` |SSL session cache size. + Provider to use, by default no provider is specified +|`session-cache-size` |int |`20480` |SSL session cache size. - @return session cache size, defaults to 1024 -|`session-timeout` |Duration |`PT30M` |SSL session timeout. + Session cache size, defaults to DEFAULT_SESSION_CACHE_SIZE. +|`session-timeout` |Duration |`PT24H` |SSL session timeout. - @return session timeout, defaults to 30 minutes + Session timeout, defaults to DEFAULT_SESSION_TIMEOUT. |`trust` |X509Certificate[] |{nbsp} |List of certificates that form the trust manager. - @return certificates to be trusted + Certificates to be trusted |`trust-all` |boolean |`false` |Trust any certificate provided by the other side of communication. This is a dangerous setting: if set to `true`, any certificate will be accepted, throwing away most of the security advantages of TLS. NEVER do this in production. - @return whether to trust all certificates, do not use in production + Whether to trust all certificates, do not use in production |`trust-manager-factory-algorithm` |string |{nbsp} |Trust manager factory algorithm. - @return algorithm to use + Algorithm to use |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_dbclient_jdbc_JdbcParametersConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_dbclient_jdbc_JdbcParametersConfig.adoc index 7fb65baf0bd..070a64f2c12 100644 --- a/docs/src/main/asciidoc/config/io_helidon_dbclient_jdbc_JdbcParametersConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_dbclient_jdbc_JdbcParametersConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -48,42 +48,42 @@ parameters |=== |key |type |default value |description -|`set-object-for-java-time` |boolean |`true` |Set all `java.time` Date/Time values directly using java.sql.PreparedStatement#setObject(int, Object). +|`set-object-for-java-time` |boolean |`true` |Set all `java.time` Date/Time values directly using java.sql.PreparedStatement.setObject(int, Object). This option shall work fine for recent JDBC drivers. Default value is `true`. - @return whether to use java.sql.PreparedStatement#setObject(int, Object) for `java.time` Date/Time values + Whether to use java.sql.PreparedStatement.setObject(int, Object) for `java.time` Date/Time values |`string-binding-size` |int |`1024` |String values with length above this limit will be bound - using java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, int) - if #useStringBinding() is set to `true`. + using java.sql.PreparedStatement.setCharacterStream(int, java.io.Reader, int) + if useStringBinding() is set to `true`. Default value is `1024`. - @return String values length limit for java.io.CharArrayReader binding -|`timestamp-for-local-time` |boolean |`true` |Use java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp) + String values length limit for java.io.CharArrayReader binding +|`timestamp-for-local-time` |boolean |`true` |Use java.sql.PreparedStatement.setTimestamp(int, java.sql.Timestamp) to set java.time.LocalTime values when `true` - or use java.sql.PreparedStatement#setTime(int, java.sql.Time) when `false`. + or use java.sql.PreparedStatement.setTime(int, java.sql.Time) when `false`. Default value is `true`. This option is vendor specific. Most of the databases are fine with java.sql.Timestamp, but for example SQL Server requires java.sql.Time. - This option does not apply when #setObjectForJavaTime() is set to `true`. + This option does not apply when setObjectForJavaTime() is set to `true`. - @return whether to use java.sql.Timestamp instead of java.sql.Time + Whether to use java.sql.Timestamp instead of java.sql.Time for java.time.LocalTime values -|`use-byte-array-binding` |boolean |`true` |Use java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream, int) binding +|`use-byte-array-binding` |boolean |`true` |Use java.sql.PreparedStatement.setBinaryStream(int, java.io.InputStream, int) binding for `byte[]` values. Default value is `true`. - @return whether to use java.io.ByteArrayInputStream binding + Whether to use java.io.ByteArrayInputStream binding |`use-n-string` |boolean |`false` |Use SQL `NCHAR`, `NVARCHAR` or `LONGNVARCHAR` value conversion for String values. Default value is `false`. - @return whether NString conversion is used -|`use-string-binding` |boolean |`true` |Use java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, int) binding - for String values with length above #stringBindingSize() limit. + Whether NString conversion is used +|`use-string-binding` |boolean |`true` |Use java.sql.PreparedStatement.setCharacterStream(int, java.io.Reader, int) binding + for String values with length above stringBindingSize() limit. Default value is `true`. - @return whether to use java.io.CharArrayReader binding + Whether to use java.io.CharArrayReader binding |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Async.adoc b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Async.adoc index 01438704109..00a78bea825 100644 --- a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Async.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Async.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,7 +44,7 @@ Type: link:{javadoc-base-url}/io.helidon.faulttolerance/io/helidon/faulttoleranc |`executor-name` |string |{nbsp} |Name of an executor service. This is only honored when service registry is used. - @return name fo the java.util.concurrent.ExecutorService to lookup + Name fo the java.util.concurrent.ExecutorService to lookup @see #executor() |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Bulkhead.adoc b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Bulkhead.adoc index 3406037da9d..9f7027f1ca2 100644 --- a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Bulkhead.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Bulkhead.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -47,12 +47,12 @@ This is a standalone configuration type, prefix from configuration root: `fault- |`limit` |int |`10` |Maximal number of parallel requests going through this bulkhead. When the limit is reached, additional requests are enqueued. - @return maximal number of parallel calls, defaults is `DEFAULT_LIMIT` + Maximal number of parallel calls, defaults is DEFAULT_LIMIT |`queue-length` |int |`10` |Maximal number of enqueued requests waiting for processing. When the limit is reached, additional attempts to invoke a request will receive a BulkheadException. - @return length of the queue + Length of the queue |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_CircuitBreaker.adoc b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_CircuitBreaker.adoc index 4829bb1fa80..d290971e69f 100644 --- a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_CircuitBreaker.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_CircuitBreaker.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,23 +46,23 @@ This is a standalone configuration type, prefix from configuration root: `fault- |`delay` |Duration |`PT5S` |How long to wait before transitioning from open to half-open state. - @return delay + Delay |`error-ratio` |int |`60` |How many failures out of 100 will trigger the circuit to open. - This is adapted to the #volume() used to handle the window of requests. + This is adapted to the volume() used to handle the window of requests. If errorRatio is 40, and volume is 10, 4 failed requests will open the circuit. - Default is `DEFAULT_ERROR_RATIO`. + Default is DEFAULT_ERROR_RATIO. - @return percent of failure that trigger the circuit to open + Percent of failure that trigger the circuit to open @see #volume() |`success-threshold` |int |`1` |How many successful calls will close a half-open circuit. Nevertheless, the first failed call will open the circuit again. - Default is `DEFAULT_SUCCESS_THRESHOLD`. + Default is DEFAULT_SUCCESS_THRESHOLD. - @return number of calls + Number of calls |`volume` |int |`10` |Rolling window size used to calculate ratio of failed requests. - Default is `DEFAULT_VOLUME`. + Default is DEFAULT_VOLUME. - @return how big a window is used to calculate error errorRatio + How big a window is used to calculate error errorRatio @see #errorRatio() |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Retry.adoc b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Retry.adoc index 86f8a4ab3f6..b1784993715 100644 --- a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Retry.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Retry.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,25 +46,25 @@ This is a standalone configuration type, prefix from configuration root: `fault- |`calls` |int |`3` |Number of calls (first try + retries). - @return number of desired calls, must be 1 (means no retries) or higher. + Number of desired calls, must be 1 (means no retries) or higher. |`delay` |Duration |`PT0.2S` |Base delay between try and retry. Defaults to `200 ms`. - @return delay between retries (combines with retry policy) -|`delay-factor` |double |`-1` |Delay retry policy factor. If unspecified (value of `-1`), Jitter retry policy would be used, unless + Delay between retries (combines with retry policy) +|`delay-factor` |double |`-1.0` |Delay retry policy factor. If unspecified (value of `-1`), Jitter retry policy would be used, unless jitter is also unspecified. Default when Retry.DelayingRetryPolicy is used is `2`. - @return delay factor for delaying retry policy + Delay factor for delaying retry policy |`jitter` |Duration |`PT-1S` |Jitter for Retry.JitterRetryPolicy. If unspecified (value of `-1`), - delaying retry policy is used. If both this value, and #delayFactor() are specified, delaying retry policy + delaying retry policy is used. If both this value, and delayFactor() are specified, delaying retry policy would be used. - @return jitter + Jitter |`overall-timeout` |Duration |`PT1S` |Overall timeout of all retries combined. - @return overall timeout + Overall timeout |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Timeout.adoc b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Timeout.adoc index f6b5156f896..1311988311f 100644 --- a/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Timeout.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_faulttolerance_Timeout.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -47,11 +47,11 @@ This is a standalone configuration type, prefix from configuration root: `fault- |`current-thread` |boolean |`false` |Flag to indicate that code must be executed in current thread instead of in an executor's thread. This flag is `false` by default. - @return whether to execute on current thread (`true`), or in an executor service (`false`}) + whether to execute on current thread (`true`), or in an executor service (`false`}) |`timeout` |Duration |`PT10S` |Duration to wait before timing out. Defaults to `10 seconds`. - @return timeout + Timeout |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_http_RequestedUriDiscoveryContext.adoc b/docs/src/main/asciidoc/config/io_helidon_http_RequestedUriDiscoveryContext.adoc index c59fc6eb724..c43c7416ba8 100644 --- a/docs/src/main/asciidoc/config/io_helidon_http_RequestedUriDiscoveryContext.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_http_RequestedUriDiscoveryContext.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -42,9 +42,9 @@ Type: link:{javadoc-base-url}/io.helidon.http/io/helidon/http/RequestedUriDiscov |=== |key |type |default value |description -|`discovery-types` |RequestedUriDiscoveryType[] (FORWARDED, X_FORWARDED, HOST) |{nbsp} |Sets the discovery types for requested URI discovery for requests arriving on the socket. -|`enabled` |boolean |`true if 'discoveryTypes' or 'trusted-proxies' is set; false otherwise` |Sets whether requested URI discovery is enabled for requestes arriving on the socket. +|`enabled` |boolean |`true if 'types' or 'trusted-proxies' is set; false otherwise` |Sets whether requested URI discovery is enabled for requestes arriving on the socket. |`trusted-proxies` |xref:{rootdir}/config/io_helidon_common_configurable_AllowList.adoc[AllowList] |{nbsp} |Sets the trusted proxies for requested URI discovery for requests arriving on the socket. +|`types` |RequestedUriDiscoveryType[] (FORWARDED, X_FORWARDED, HOST) |{nbsp} |Sets the discovery types for requested URI discovery for requests arriving on the socket. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_http_encoding_ContentEncodingContext.adoc b/docs/src/main/asciidoc/config/io_helidon_http_encoding_ContentEncodingContext.adoc index 02af5eb188f..fc67019499f 100644 --- a/docs/src/main/asciidoc/config/io_helidon_http_encoding_ContentEncodingContext.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_http_encoding_ContentEncodingContext.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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,7 +45,7 @@ Type: link:{javadoc-base-url}/io.helidon.http.encoding/io/helidon/http/encoding/ |`content-encodings` |io.helidon.http.encoding.ContentEncoding[] (service provider interface) |{nbsp} |List of content encodings that should be used. Encodings configured here have priority over encodings discovered through service loader. - @return list of content encodings to be used (such as `gzip,deflate`) + List of content encodings to be used (such as `gzip,deflate`) |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_http_media_MediaContext.adoc b/docs/src/main/asciidoc/config/io_helidon_http_media_MediaContext.adoc index 7a7804d6903..ffafeed8c35 100644 --- a/docs/src/main/asciidoc/config/io_helidon_http_media_MediaContext.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_http_media_MediaContext.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,16 +44,16 @@ Type: link:{javadoc-base-url}/io.helidon.http.media/io/helidon/http/media/MediaC |`fallback` |xref:{rootdir}/config/io_helidon_http_media_MediaContext.adoc[MediaContext] |{nbsp} |Existing context to be used as a fallback for this context. - @return media context to use if supports configured on this request cannot provide a good result + Media context to use if supports configured on this request cannot provide a good result |`media-supports` |io.helidon.http.media.MediaSupport[] (service provider interface) |{nbsp} |Media supports to use. This instance has priority over provider(s) discovered by service loader. The providers are used in order of calling this method, where the first support added is the first one to be queried for readers and writers. - @return media supports + Media supports |`register-defaults` |boolean |`true` |Should we register defaults of Helidon, such as String media support. - @return whether to register default media supports + Whether to register default media supports |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_neo4j_Neo4j.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_neo4j_Neo4j.adoc index 610b363c47c..04274bfc301 100644 --- a/docs/src/main/asciidoc/config/io_helidon_integrations_neo4j_Neo4j.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_neo4j_Neo4j.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,10 +44,10 @@ Type: link:{javadoc-base-url}/io.helidon.integrations.neo4j/io/helidon/integrati |`authentication-enabled` |boolean |`true` |Enable authentication. |`certificate` |Path |{nbsp} |Set certificate path. -|`connection-acquisition-timeout` |Duration |`PT1MS` |Set connection acquisition timeout. +|`connection-acquisition-timeout` |Duration |`PT1M` |Set connection acquisition timeout. |`encrypted` |boolean |{nbsp} |Enable encrypted field. |`hostname-verification-enabled` |boolean |{nbsp} |Enable hostname verification. -|`idle-time-before-connection-test` |Duration |`PT-1MS` |Set idle time. +|`idle-time-before-connection-test` |Duration |`PT1MS` |Set idle time. |`log-leaked-sessions` |boolean |{nbsp} |Enable log leaked sessions. |`max-connection-lifetime` |Duration |`PT5H` |Set max life time. |`max-connection-pool-size` |int |`100` |Set pool size. diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigFileStrategyConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigFileStrategyConfig.adoc new file mode 100644 index 00000000000..59be6bf69d8 --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigFileStrategyConfig.adoc @@ -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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.integrations.oci.ConfigFileStrategyConfig +:keywords: helidon, config, io.helidon.integrations.oci.ConfigFileStrategyConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.integrations.oci.ConfigFileStrategyConfig +include::{rootdir}/includes/attributes.adoc[] + += ConfigFileStrategyConfig (integrations.oci) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.integrations.oci/io/helidon/integrations/oci/ConfigFileStrategyConfig.html[io.helidon.integrations.oci.ConfigFileStrategyConfig] + + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`path` |string |{nbsp} |The OCI configuration profile path. + + The OCI configuration profile path +|`profile` |string |`DEFAULT` |The OCI configuration/auth profile name. + + The optional OCI configuration/auth profile name + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigStrategyConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigStrategyConfig.adoc new file mode 100644 index 00000000000..57161f7b17f --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_ConfigStrategyConfig.adoc @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.integrations.oci.ConfigStrategyConfig +:keywords: helidon, config, io.helidon.integrations.oci.ConfigStrategyConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.integrations.oci.ConfigStrategyConfig +include::{rootdir}/includes/attributes.adoc[] + += ConfigStrategyConfig (integrations.oci) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.integrations.oci/io/helidon/integrations/oci/ConfigStrategyConfig.html[io.helidon.integrations.oci.ConfigStrategyConfig] + + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`fingerprint` |string |{nbsp} |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. + + The OCI authentication fingerprint +|`passphrase` |char[] |{nbsp} |The OCI authentication passphrase. + + This property must be provided in order to set the + {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPassphraseCharacters()}. + + The OCI authentication passphrase +|`private-key` |xref:{rootdir}/config/io_helidon_common_configurable_Resource.adoc[Resource] |{nbsp} |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 `.oci/oic_api_key.pem` file in user home directory. + + The OCI authentication key file +|`region` |string |{nbsp} |The OCI region. + + The OCI region +|`tenant-id` |string |{nbsp} |The OCI tenant id. + + This property must be provided in order to set the + {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getTenantId()}. + + The OCI tenant id +|`user-id` |string |{nbsp} |The OCI user id. + + This property must be provided in order to set the + {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getUserId()}. + + The OCI user id + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_OciConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_OciConfig.adoc new file mode 100644 index 00000000000..d6fb4643cd8 --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_OciConfig.adoc @@ -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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.integrations.oci.OciConfig +:keywords: helidon, config, io.helidon.integrations.oci.OciConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.integrations.oci.OciConfig +include::{rootdir}/includes/attributes.adoc[] + += OciConfig (integrations.oci) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.integrations.oci/io/helidon/integrations/oci/OciConfig.html[io.helidon.integrations.oci.OciConfig] + + +This is a standalone configuration type, prefix from configuration root: `helidon.oci` + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`allowed-atn-strategies` |string[] |{nbsp} |List of attempted authentication strategies in case atnStrategy() is set to STRATEGY_AUTO. + + In case the list is empty, all available strategies will be tried, ordered by their io.helidon.common.Weight + + List of authentication strategies to be tried + @see #atnStrategy() +|`atn-strategy` |string |`auto` |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: + +- STRATEGY_AUTO - use the list of allowedAtnStrategies() (in the provided order), and choose + the first one + capable of providing data +- AtnStrategyConfig.STRATEGY - + use configuration of the application to obtain values needed to set up connectivity, uses + com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider +- AtnStrategyConfigFile.STRATEGY - use configuration file of OCI (`home/.oci/config`), uses + com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider +- AtnStrategyResourcePrincipal.STRATEGY - use identity of the OCI resource the service is executed on + (fn), uses + com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider +- AtnStrategyInstancePrincipal.STRATEGY - use identity of the OCI instance the service is running on, uses + com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider + +The authentication strategy to apply +|`atn-timeout` |Duration |`PT10S` |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. + + Authentication operation timeout +|`config-file-strategy` |xref:{rootdir}/config/io_helidon_integrations_oci_ConfigFileStrategyConfig.adoc[ConfigFileStrategyConfig] |{nbsp} |Config file strategy configuration (if provided and used). + + Information to customize config for atnStrategy() +|`config-strategy` |xref:{rootdir}/config/io_helidon_integrations_oci_ConfigStrategyConfig.adoc[ConfigStrategyConfig] |{nbsp} |Config strategy configuration (if provided and used). + + Information needed for config atnStrategy() +|`imds-base-uri` |URI |{nbsp} |The OCI IMDS URI (http URL pointing to the metadata service, if customization needed. + + The OCI IMDS URI +|`imds-timeout` |Duration |`PT0.1S` |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. + + The OCI IMDS connection timeout +|`region` |Region |{nbsp} |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. + + Explicit region + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_metrics_OciMetricsSupport.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_metrics_OciMetricsSupport.adoc index cb2a64ae0b3..b75534ef5a4 100644 --- a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_metrics_OciMetricsSupport.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_metrics_OciMetricsSupport.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,12 +43,12 @@ Type: link:{javadoc-base-url}/io.helidon.integrations.oci.metrics/io/helidon/int |key |type |default value |description |`batch-delay` |long |`1` |Sets the delay interval if metrics are posted in batches - (defaults to `DEFAULT_BATCH_DELAY`). + (defaults to DEFAULT_BATCH_DELAY). |`batch-size` |int |`50` |Sets the maximum no. of metrics to send in a batch - (defaults to `DEFAULT_BATCH_SIZE`). + (defaults to DEFAULT_BATCH_SIZE). |`compartment-id` |string |{nbsp} |Sets the compartment ID. |`delay` |long |`60` |Sets the delay interval between metric posting - (defaults to `DEFAULT_SCHEDULER_DELAY`). + (defaults to DEFAULT_SCHEDULER_DELAY). |`description-enabled` |boolean |`true` |Sets whether the description should be enabled or not. Defaults to `true`. @@ -58,7 +58,7 @@ Type: link:{javadoc-base-url}/io.helidon.integrations.oci.metrics/io/helidon/int Defaults to `true`. |`initial-delay` |long |`1` |Sets the initial delay before metrics are sent to OCI - (defaults to `DEFAULT_SCHEDULER_INITIAL_DELAY`). + (defaults to DEFAULT_SCHEDULER_INITIAL_DELAY). |`namespace` |string |{nbsp} |Sets the namespace. |`resource-group` |string |{nbsp} |Sets the resource group. |`scheduling-time-unit` |TimeUnit (NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS) |`TimeUnit.SECONDS` |Sets the time unit applied to the initial delay and delay values (defaults to `TimeUnit.SECONDS`). diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_sdk_runtime_OciConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_sdk_runtime_OciConfig.adoc index 0f80ead9263..3a9d92edfe1 100644 --- a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_sdk_runtime_OciConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_sdk_runtime_OciConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,7 +46,7 @@ This is a standalone configuration type, prefix from configuration root: `oci` |`auth-strategies` |string[] (auto, config, config-file, instance-principals, resource-principal) |{nbsp} |The list of authentication strategies that will be attempted by com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider when one is - called for. This is only used if #authStrategy() is not present. + called for. This is only used if authStrategy() is not present. - `auto` - if present in the list, or if no value for this property exists. @@ -68,111 +68,111 @@ This is a standalone configuration type, prefix from configuration root: `oci` If there are more than one strategy descriptors defined, the first one that is deemed to be available/suitable will be used and all others will be ignored. - @return the list of authentication strategies that will be applied, defaulting to `auto` + The list of authentication strategies that will be applied, defaulting to `auto` @see io.helidon.integrations.oci.sdk.runtime.OciAuthenticationDetailsProvider.AuthStrategy -|`auth-strategy` |string (auto, config, config-file, instance-principals, resource-principal) |{nbsp} |The singular authentication strategy to apply. This will be preferred over #authStrategies() if both are +|`auth-strategy` |string (auto, config, config-file, instance-principals, resource-principal) |{nbsp} |The singular authentication strategy to apply. This will be preferred over authStrategies() if both are present. - @return the singular authentication strategy to be applied + The singular authentication strategy to be applied |`auth.fingerprint` |string |{nbsp} |The OCI authentication fingerprint. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, this 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 + The OCI authentication fingerprint |`auth.keyFile` |string |`oci_api_key.pem` |The OCI authentication key file. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, this property must be provided in order to set the {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPrivateKey()}. This file must exist in the - `user.home` directory. Alternatively, this property can be set using either #authPrivateKey() or - using #authPrivateKeyPath(). + `user.home` directory. Alternatively, this property can be set using either authPrivateKey() or + using authPrivateKeyPath(). - @return the OCI authentication key file + The OCI authentication key file |`auth.passphrase` |char[] |{nbsp} |The OCI authentication passphrase. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, this property must be provided in order to set the {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPassphraseCharacters()}. - @return the OCI authentication passphrase + The OCI authentication passphrase |`auth.private-key` |char[] |{nbsp} |The OCI authentication private key. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, this property must be provided in order to set the {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPrivateKey()}. Alternatively, this property - can be set using either #authKeyFile() residing in the `user.home` directory, or using - #authPrivateKeyPath(). + can be set using either authKeyFile() residing in the `user.home` directory, or using + authPrivateKeyPath(). - @return the OCI authentication private key + The OCI authentication private key |`auth.private-key-path` |string |{nbsp} |The OCI authentication key file path. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, this property must be provided in order to set the {@linkplain com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getPrivateKey()}. This file path is - an alternative for using #authKeyFile() where the file must exist in the `user.home` directory. - Alternatively, this property can be set using #authPrivateKey(). + an alternative for using authKeyFile() where the file must exist in the `user.home` directory. + Alternatively, this property can be set using authPrivateKey(). - @return the OCI authentication key file path + The OCI authentication key file path |`auth.region` |string |{nbsp} |The OCI region. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, either this property or com.oracle.bmc.auth.RegionProvider must be provide a value in order to set the {@linkplain com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider#getRegion()}. - @return the OCI region + The OCI region |`auth.tenant-id` |string |{nbsp} |The OCI tenant id. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #simpleConfigIsPresent(). + present in the value for the authStrategies(). This is also known as simpleConfigIsPresent(). When it is present, this property must be provided in order to set the {@linkplain com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider#getTenantId()}. - @return the OCI tenant id + The OCI tenant id |`auth.user-id` |string |{nbsp} |The OCI user id. This configuration property has an effect only when `config` is, explicitly or implicitly, - present in the value for the #authStrategies(). + present in the value for the authStrategies(). When it is present, this property must be provided in order to set the {@linkplain com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider#getUserId()}. - @return the OCI user id + The OCI user id |`config.path` |string |{nbsp} |The OCI configuration profile path. This configuration property has an effect only when `config-file` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #fileConfigIsPresent(). + present in the value for the authStrategies(). This is also known as fileConfigIsPresent(). When it is present, this property must also be present and then the {@linkplain com.oracle.bmc.ConfigFileReader#parse(String)} method will be passed this value. It is expected to be passed with a valid OCI configuration file path. - @return the OCI configuration profile path + The OCI configuration profile path |`config.profile` |string |`DEFAULT` |The OCI configuration/auth profile name. This configuration property has an effect only when `config-file` is, explicitly or implicitly, - present in the value for the #authStrategies(). This is also known as #fileConfigIsPresent(). + present in the value for the authStrategies(). This is also known as fileConfigIsPresent(). When it is present, this property may also be optionally provided in order to override the default - `DEFAULT_PROFILE_NAME`. + DEFAULT_PROFILE_NAME. - @return the optional OCI configuration/auth profile name + The optional OCI configuration/auth profile name |`imds.hostname` |string |`169.254.169.254` |The OCI IMDS hostname. This configuration property is used to identify the metadata service url. - @return the OCI IMDS hostname + The OCI IMDS hostname |`imds.timeout.milliseconds` |Duration |`PT0.1S` |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 + The OCI IMDS connection timeout @see OciAvailability |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_oci_tls_certificates_OciCertificatesTlsManager.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_tls_certificates_OciCertificatesTlsManager.adoc new file mode 100644 index 00000000000..3cac3de9438 --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_oci_tls_certificates_OciCertificatesTlsManager.adoc @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.integrations.oci.tls.certificates.OciCertificatesTlsManager +:keywords: helidon, config, io.helidon.integrations.oci.tls.certificates.OciCertificatesTlsManager +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.integrations.oci.tls.certificates.OciCertificatesTlsManager +include::{rootdir}/includes/attributes.adoc[] + += OciCertificatesTlsManager (integrations.oci.tls.certificates) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.integrations.oci.tls.certificates/io/helidon/integrations/oci/tls/certificates/OciCertificatesTlsManager.html[io.helidon.integrations.oci.tls.certificates.OciCertificatesTlsManager] + + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`ca-ocid` |string |{nbsp} |The Certificate Authority OCID. + + Certificate authority OCID +|`cert-ocid` |string |{nbsp} |The Certificate OCID. + + Certificate OCID +|`compartment-ocid` |string |{nbsp} |The OCID of the compartment the services are in. + + The compartment OCID +|`key-ocid` |string |{nbsp} |The Key OCID. + + Key OCID +|`key-password` |Supplier |{nbsp} |The Key password. + + Key password +|`schedule` |string |{nbsp} |The schedule for trigger a reload check, testing whether there is a new io.helidon.common.tls.Tls instance + available. + + The schedule for reload +|`vault-crypto-endpoint` |URI |{nbsp} |The address to use for the OCI Key Management Service / Vault crypto usage. + Each OCI Vault has public crypto and management endpoints. We need to specify the crypto endpoint of the vault we are + rotating the private keys in. The implementation expects both client and server to store the private key in the same vault. + + The address for the key management service / vault crypto usage +|`vault-management-endpoint` |URI |{nbsp} |The address to use for the OCI Key Management Service / Vault management usage. + The crypto endpoint of the vault we are rotating the private keys in. + + The address for the key management service / vault management usage + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_integrations_openapi_ui_OpenApiUi.adoc b/docs/src/main/asciidoc/config/io_helidon_integrations_openapi_ui_OpenApiUi.adoc index 1e71e989907..53fd3897ad5 100644 --- a/docs/src/main/asciidoc/config/io_helidon_integrations_openapi_ui_OpenApiUi.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_integrations_openapi_ui_OpenApiUi.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,13 +44,13 @@ Type: link:{javadoc-base-url}/io.helidon.integrations.openapi.ui/io/helidon/inte |`enabled` |boolean |`true` |Sets whether the service should be enabled. - @return `true` if enabled, `false` otherwise + `true` if enabled, `false` otherwise |`options` |Map<string, string> |{nbsp} |Merges implementation-specific UI options. - @return options for the UI to merge + Options for the UI to merge |`web-context` |string |{nbsp} |Full web context (not just the suffix). - @return full web context path + Full web context path |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_metrics_api_ComponentMetricsSettings_Builder.adoc b/docs/src/main/asciidoc/config/io_helidon_metrics_api_ComponentMetricsSettings_Builder.adoc new file mode 100644 index 00000000000..7fb5649a7df --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_metrics_api_ComponentMetricsSettings_Builder.adoc @@ -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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.metrics.api.ComponentMetricsSettings.Builder +:keywords: helidon, config, io.helidon.metrics.api.ComponentMetricsSettings.Builder +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.metrics.api.ComponentMetricsSettings.Builder +include::{rootdir}/includes/attributes.adoc[] + += Builder (metrics.api.ComponentMetricsSettings) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.metrics.api.ComponentMetricsSettings/io/helidon/metrics/api/ComponentMetricsSettings/Builder.html[io.helidon.metrics.api.ComponentMetricsSettings.Builder] + + +[source,text] +.Config key +---- +metrics +---- + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`enabled` |boolean |{nbsp} |Sets whether metrics should be enabled for the component. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsConfig.adoc index 5b50e8adfc7..64b33e47f53 100644 --- a/docs/src/main/asciidoc/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,10 +44,10 @@ Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/KeyP |`extended` |boolean |`false` |Whether KPI extended metrics are enabled. - @return true if KPI extended metrics are enabled; false otherwise + True if KPI extended metrics are enabled; false otherwise |`long-running-requests.threshold` |Duration |`PT10S` |Threshold in ms that characterizes whether a request is long running. - @return threshold in ms indicating a long-running request + Threshold in ms indicating a long-running request |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_metrics_api_MetricsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_metrics_api_MetricsConfig.adoc index b31c8e4f1c9..97b5bfeae80 100644 --- a/docs/src/main/asciidoc/config/io_helidon_metrics_api_MetricsConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_metrics_api_MetricsConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,32 +46,32 @@ This is a standalone configuration type, prefix from configuration root: `metric |`app-name` |string |{nbsp} |Value for the application tag to be added to each meter ID. - @return application tag value + Application tag value |`app-tag-name` |string |{nbsp} |Name for the application tag to be added to each meter ID. - @return application tag name + Application tag name |`enabled` |boolean |`true` |Whether metrics functionality is enabled. - @return if metrics are configured to be enabled + If metrics are configured to be enabled |`key-performance-indicators` |xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsConfig.adoc[KeyPerformanceIndicatorMetricsConfig] |{nbsp} |Key performance indicator metrics settings. - @return key performance indicator metrics settings -|`permit-all` |boolean |{nbsp} |Whether to allow anybody to access the endpoint. + Key performance indicator metrics settings +|`permit-all` |boolean |`true` |Whether to allow anybody to access the endpoint. - @return whether to permit access to metrics endpoint to anybody, defaults to `true` + Whether to permit access to metrics endpoint to anybody, defaults to `true` @see #roles() |`rest-request-enabled` |boolean |{nbsp} |Whether automatic REST request metrics should be measured. - @return true/false -|`roles` |string[] |{nbsp} |Hints for role names the user is expected to be in. + True/false +|`roles` |string[] |`observe` |Hints for role names the user is expected to be in. - @return list of hints + List of hints |`scoping` |xref:{rootdir}/config/io_helidon_metrics_api_ScopingConfig.adoc[ScopingConfig] |{nbsp} |Settings related to scoping management. - @return scoping settings + Scoping settings |`tags` |xref:{rootdir}/config/io_helidon_metrics_api_Tag.adoc[Tag[]] |{nbsp} |Global tags. - @return name/value pairs for global tags + Name/value pairs for global tags |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopeConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopeConfig.adoc index e39511a24d0..f1caf5e0661 100644 --- a/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopeConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopeConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,16 +44,16 @@ Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/Scop |`enabled` |boolean |`true` |Whether the scope is enabled. - @return if the scope is enabled + If the scope is enabled |`filter.exclude` |Pattern |{nbsp} |Regular expression for meter names to exclude. - @return exclude expression + Exclude expression |`filter.include` |Pattern |{nbsp} |Regular expression for meter names to include. - @return include expression + Include expression |`name` |string |{nbsp} |Name of the scope to which the configuration applies. - @return scope name + Scope name |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopingConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopingConfig.adoc index fa917fdda5f..a746c09d1bf 100644 --- a/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopingConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_metrics_api_ScopingConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,15 +43,15 @@ Type: link:{javadoc-base-url}/io.helidon.metrics.api/io/helidon/metrics/api/Scop |key |type |default value |description |`default` |string |`application` |Default scope value to associate with meters that are registered without an explicit setting; no setting means meters - are assigned scope `io.helidon.metrics.api.Meter.Scope#DEFAULT`. + are assigned scope io.helidon.metrics.api.Meter.Scope.DEFAULT. - @return default scope value + Default scope value |`scopes` |xref:{rootdir}/config/io_helidon_metrics_api_ScopeConfig.adoc[Map<string, ScopeConfig>] |{nbsp} |Settings for individual scopes. - @return scope settings + Scope settings |`tag-name` |string |`scope` |Tag name for storing meter scope values in the underlying implementation meter registry. - @return tag name for storing scope values + Tag name for storing scope values |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_microprofile_openapi_MpOpenApiManagerConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_microprofile_openapi_MpOpenApiManagerConfig.adoc index 236a5fdc271..23d30831f44 100644 --- a/docs/src/main/asciidoc/config/io_helidon_microprofile_openapi_MpOpenApiManagerConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_microprofile_openapi_MpOpenApiManagerConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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,7 +45,7 @@ Type: link:{javadoc-base-url}/io.helidon.microprofile.openapi/io/helidon/micropr |`mp.openapi.extensions.helidon.use-jaxrs-semantics` |boolean |{nbsp} |If `true` and the `jakarta.ws.rs.core.Application` class returns a non-empty set, endpoints defined by other resources are not included in the OpenAPI document. - @return `true` if enabled, `false` otherwise + `true` if enabled, `false` otherwise |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_openapi_OpenApiFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_openapi_OpenApiFeature.adoc index 888e80f9c0e..ddf2e1a45bf 100644 --- a/docs/src/main/asciidoc/config/io_helidon_openapi_OpenApiFeature.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_openapi_OpenApiFeature.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -33,6 +33,10 @@ Type: link:{javadoc-base-url}/io.helidon.openapi/io/helidon/openapi/OpenApiFeatu This is a standalone configuration type, prefix from configuration root: `openapi` +This type provides the following service implementations: + +- `io.helidon.webserver.spi.ServerFeatureProvider` + == Configuration options @@ -46,29 +50,36 @@ This is a standalone configuration type, prefix from configuration root: `openap |`cors` |xref:{rootdir}/config/io_helidon_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |CORS config. - @return CORS config + CORS config |`enabled` |boolean |`true` |Sets whether the feature should be enabled. - @return `true` if enabled, `false` otherwise + `true` if enabled, `false` otherwise |`manager` |io.helidon.openapi.OpenApiManager (service provider interface) |{nbsp} |OpenAPI manager. - @return the OpenAPI manager -|`permit-all` |boolean |{nbsp} |Whether to allow anybody to access the endpoint. + The OpenAPI manager +|`permit-all` |boolean |`true` |Whether to allow anybody to access the endpoint. - @return whether to permit access to metrics endpoint to anybody, defaults to `true` + Whether to permit access to metrics endpoint to anybody, defaults to `true` @see #roles() -|`roles` |string[] |{nbsp} |Hints for role names the user is expected to be in. +|`roles` |string[] |`openapi` |Hints for role names the user is expected to be in. - @return list of hints + List of hints |`services` |io.helidon.openapi.OpenApiService[] (service provider interface) |{nbsp} |OpenAPI services. - @return the OpenAPI services + The OpenAPI services +|`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets. + + Socket names to register on, defaults to empty (all available sockets) |`static-file` |string |{nbsp} |Path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`. - @return location of the static OpenAPI document file + Location of the static OpenAPI document file |`web-context` |string |`/openapi` |Web context path for the OpenAPI endpoint. - @return webContext to use + WebContext to use +|`weight` |double |`90.0` |Weight of the OpenAPI feature. This is quite low, to be registered after routing. + io.helidon.openapi.OpenApiFeature.WEIGHT. + + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_scheduling_Cron.adoc b/docs/src/main/asciidoc/config/io_helidon_scheduling_Cron.adoc index c8a1de2f836..afbb32ab850 100644 --- a/docs/src/main/asciidoc/config/io_helidon_scheduling_Cron.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_scheduling_Cron.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -47,7 +47,7 @@ Type: link:{javadoc-base-url}/io.helidon.scheduling/io/helidon/scheduling/Cron.h - `0 45 9 ? * *` - Every day at 9:45 - `0 15 8 ? * MON-FRI` - Every workday at 8:15 -@return cron expression +Cron expression |=== @@ -62,7 +62,7 @@ Type: link:{javadoc-base-url}/io.helidon.scheduling/io/helidon/scheduling/Cron.h |`concurrent` |boolean |`true` |Allow concurrent execution if previous task didn't finish before next execution. Default value is `true`. - @return true for allow concurrent execution. + True for allow concurrent execution. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_scheduling_FixedRate.adoc b/docs/src/main/asciidoc/config/io_helidon_scheduling_FixedRate.adoc index 028643cab43..301fd9df2f1 100644 --- a/docs/src/main/asciidoc/config/io_helidon_scheduling_FixedRate.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_scheduling_FixedRate.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -39,10 +39,10 @@ Type: link:{javadoc-base-url}/io.helidon.scheduling/io/helidon/scheduling/FixedR |=== |key |type |default value |description -|`delay` |long |{nbsp} |Fixed rate delay between each invocation. Time unit is by default java.util.concurrent.TimeUnit#SECONDS, - can be specified with io.helidon.scheduling.Scheduling.FixedRateBuilder#timeUnit(java.util.concurrent.TimeUnit). +|`delay` |long |{nbsp} |Fixed rate delay between each invocation. Time unit is by default java.util.concurrent.TimeUnit.SECONDS, + can be specified with io.helidon.scheduling.FixedRateConfig.Builder.timeUnit(java.util.concurrent.TimeUnit). - @return delay between each invocation + Delay between each invocation |=== @@ -54,22 +54,22 @@ Type: link:{javadoc-base-url}/io.helidon.scheduling/io/helidon/scheduling/FixedR |=== |key |type |default value |description -|`delay-type` |DelayType (SINCE_PREVIOUS_START, SINCE_PREVIOUS_END) |`@io.helidon.scheduling.FixedRate.DelayType@.SINCE_PREVIOUS_START` |Configure whether the delay between the invocations should be calculated from the time when previous task started or ended. - Delay type is by default FixedRate.DelayType#SINCE_PREVIOUS_START. +|`delay-type` |DelayType |`DelayType.SINCE_PREVIOUS_START` |Configure whether the delay between the invocations should be calculated from the time when previous task started or ended. + Delay type is by default FixedRate.DelayType.SINCE_PREVIOUS_START. - @return delay type -|`initial-delay` |long |`0` |Initial delay of the first invocation. Time unit is by default java.util.concurrent.TimeUnit#SECONDS, + Delay type +|`initial-delay` |long |`0` |Initial delay of the first invocation. Time unit is by default java.util.concurrent.TimeUnit.SECONDS, can be specified with - io.helidon.scheduling.Scheduling.FixedRateBuilder#timeUnit(java.util.concurrent.TimeUnit) timeUnit(). + io.helidon.scheduling.FixedRateConfig.Builder.timeUnit(java.util.concurrent.TimeUnit) timeUnit(). - @return initial delay value -|`time-unit` |TimeUnit (NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS) |`TimeUnit.SECONDS` |java.util.concurrent.TimeUnit TimeUnit used for interpretation of values provided with - io.helidon.scheduling.Scheduling.FixedRateBuilder#delay(long) - and io.helidon.scheduling.Scheduling.FixedRateBuilder#initialDelay(long). + Initial delay value +|`time-unit` |TimeUnit |`TimeUnit.TimeUnit.SECONDS` |java.util.concurrent.TimeUnit TimeUnit used for interpretation of values provided with + io.helidon.scheduling.FixedRateConfig.Builder.delay(long) + and io.helidon.scheduling.FixedRateConfig.Builder.initialDelay(long). - @return time unit for interpreting values - in io.helidon.scheduling.Scheduling.FixedRateBuilder#delay(long) - and io.helidon.scheduling.Scheduling.FixedRateBuilder#initialDelay(long) + Time unit for interpreting values + in io.helidon.scheduling.FixedRateConfig.Builder.delay(long) + and io.helidon.scheduling.FixedRateConfig.Builder.initialDelay(long) |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_Security.adoc b/docs/src/main/asciidoc/config/io_helidon_security_Security.adoc index 77fa7bce369..7aa7f794246 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_Security.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_Security.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -48,16 +48,16 @@ This is a standalone configuration type, prefix from configuration root: `securi Such as: - xref:{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProvider.adoc[idcs-role-mapper (IdcsRoleMapperProvider)] + - xref:{rootdir}/config/io_helidon_security_providers_jwt_JwtProvider.adoc[jwt (JwtProvider)] - xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc[http-basic-auth (HttpBasicAuthProvider)] - xref:{rootdir}/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperProvider.adoc[idcs-role-mapper (IdcsMtRoleMapperProvider)] - xref:{rootdir}/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc[google-login (GoogleTokenProvider)] - xref:{rootdir}/config/io_helidon_security_providers_oidc_OidcProvider.adoc[oidc (OidcProvider)] - xref:{rootdir}/config/io_helidon_security_providers_httpauth_HttpDigestAuthProvider.adoc[http-digest-auth (HttpDigestAuthProvider)] - - xref:{rootdir}/config/io_helidon_security_providers_jwt_JwtProvider.adoc[jwt (JwtProvider)] - xref:{rootdir}/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc[header-atn (HeaderAtnProvider)] - xref:{rootdir}/config/io_helidon_security_providers_abac_AbacProvider.adoc[abac (AbacProvider)] - |{nbsp} |Add a provider, works as #addProvider(io.helidon.security.spi.SecurityProvider, String), where the name is set + |{nbsp} |Add a provider, works as addProvider(io.helidon.security.spi.SecurityProvider, String), where the name is set to `Class#getSimpleName()`. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_SecurityTime.adoc b/docs/src/main/asciidoc/config/io_helidon_security_SecurityTime.adoc index 6a2864a669c..e698c1b2fab 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_SecurityTime.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_SecurityTime.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -42,18 +42,18 @@ Type: link:{javadoc-base-url}/io.helidon.security/io/helidon/security/SecurityTi |=== |key |type |default value |description -|`day-of-month` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). -|`hour-of-day` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). -|`millisecond` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). -|`minute` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). -|`month` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). -|`second` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). +|`day-of-month` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). +|`hour-of-day` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). +|`millisecond` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). +|`minute` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). +|`month` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). +|`second` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). |`shift-by-seconds` |long |`0` |Configure a time-shift in seconds, to move the current time to past or future. |`time-zone` |ZoneId |{nbsp} |Override current time zone. The time will represent the SAME instant, in an explicit timezone. If we are in a UTC time zone and you set the timezone to "Europe/Prague", the time will be shifted by the offset of Prague (e.g. if it is noon right now in UTC, you would get 14:00). -|`year` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField#YEAR). +|`year` |long |{nbsp} |Set an explicit value for one of the time fields (such as ChronoField.YEAR). |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_common_EvictableCache.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_common_EvictableCache.adoc index 6cd6017d009..e58103fa272 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_common_EvictableCache.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_common_EvictableCache.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,7 +49,7 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.common/io/helidon/se |`cache-overall-timeout-millis` |long |`3600000` |Configure record timeout since its creation. |`cache-timeout-millis` |long |`3600000` |Configure record timeout since last access. |`evictor-class` |Class |{nbsp} |Configure evictor to check if a record is still valid. - This should be a fast way to check, as it is happening in a ConcurrentHashMap#forEachKey(long, Consumer). + This should be a fast way to check, as it is happening in a ConcurrentHashMap.forEachKey(long, Consumer). This is also called during all get and remove operations to only return valid records. |`max-size` |long |`100000` |Configure maximal cache size. |`parallelism-threshold` |long |`10000` |Configure parallelism threshold. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc index d963e1c66ff..d9b8791fab3 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_google_login_GoogleTokenProvider.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -56,7 +56,7 @@ This type provides the following service implementations: |key |type |default value |description |`client-id` |string |{nbsp} |Google application client id, to validate that the token was generated by Google for us. -|`optional` |boolean |`false` |If set to true, this provider will return io.helidon.security.SecurityResponse.SecurityStatus#ABSTAIN instead +|`optional` |boolean |`false` |If set to true, this provider will return io.helidon.security.SecurityResponse.SecurityStatus.ABSTAIN instead of failing in case of invalid request. |`outbound` |xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig] |{nbsp} |Outbound configuration - a set of outbound targets that will have the token propagated. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc index 59ae9e6314f..cf267481c23 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_header_HeaderAtnProvider.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -62,7 +62,7 @@ This type provides the following service implementations: If set to false, request will process and this provider will abstain. |`outbound` |xref:{rootdir}/config/io_helidon_security_providers_common_OutboundTarget.adoc[OutboundTarget[]] |{nbsp} |Configure outbound target for identity propagation. |`outbound-token` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |Token handler to create outbound headers to propagate identity. - If not defined, #atnTokenHandler will be used. + If not defined, atnTokenHandler will be used. |`principal-type` |SubjectType (USER, SERVICE) |`USER` |Principal type this provider extracts (and also propagates). |`propagate` |boolean |`false` |Whether to propagate identity. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc index da86bba2f18..9ba61b0ab0b 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_httpauth_HttpBasicAuthProvider.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -62,7 +62,7 @@ This type provides the following service implementations: |`principal-type` |SubjectType (USER, SERVICE) |`USER` |Principal type this provider extracts (and also propagates). |`realm` |string |`helidon` |Set the realm to use when challenging users. |`users` |xref:{rootdir}/config/io_helidon_security_providers_httpauth_ConfigUserStore_ConfigUser.adoc[ConfigUser[]] |{nbsp} |Set user store to validate users. - Removes any other stores added through #addUserStore(SecureUserStore). + Removes any other stores added through addUserStore(SecureUserStore). |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc index e7f63812a94..4135b525654 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_HttpSignProvider.adoc @@ -77,20 +77,20 @@ This type provides the following service implementations: keys: [ { key-id = "service1" - hmac.secret = "${CLEAR=changeit}" + hmac.secret = "${CLEAR=password}" }] } } } |`optional` |boolean |`true` |Set whether the signature is optional. If set to true (default), this provider will - SecurityResponse.SecurityStatus#ABSTAIN from this request if signature is not - present. If set to false, this provider will SecurityResponse.SecurityStatus#FAILURE fail + SecurityResponse.SecurityStatus.ABSTAIN from this request if signature is not + present. If set to false, this provider will SecurityResponse.SecurityStatus.FAILURE fail if signature is not present. |`outbound` |xref:{rootdir}/config/io_helidon_security_providers_common_OutboundConfig.adoc[OutboundConfig] |{nbsp} |Add outbound targets to this builder. The targets are used to chose what to do for outbound communication. The targets should have OutboundTargetDefinition attached through - OutboundTarget.Builder#customObject(Class, Object) to tell us how to sign + OutboundTarget.Builder.customObject(Class, Object) to tell us how to sign the request. The same can be done through configuration: @@ -108,21 +108,21 @@ This type provides the following service implementations: # This configures the OutboundTargetDefinition signature { key-id = "service1" - hmac.secret = "${CLEAR=changeit}" + hmac.secret = "${CLEAR=password}" } }] } } |`realm` |string |`helidon` |Realm to use for challenging inbound requests that do not have "Authorization" header - in case header is HttpSignHeader#AUTHORIZATION and singatures are not optional. + in case header is HttpSignHeader.AUTHORIZATION and singatures are not optional. |`sign-headers` |xref:{rootdir}/config/io_helidon_security_providers_httpsign_SignedHeadersConfig_HeadersConfig.adoc[HeadersConfig[]] |{nbsp} |Override the default inbound required headers (e.g. headers that MUST be signed and headers that MUST be signed IF present). Defaults: - get, head, delete methods: date, (request-target), host are mandatory; authorization if present (unless we are - creating/validating the HttpSignHeader#AUTHORIZATION ourselves + creating/validating the HttpSignHeader.AUTHORIZATION ourselves - put, post: same as above, with addition of: content-length, content-type and digest if present - for other methods: date, (request-target) diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc index 02d5ee7aca7..9f134d63f49 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_httpsign_InboundClientDefinition.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,8 +49,8 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.httpsign/io/helidon/ - hmac-sha256 - symmetric based on a shared secret -|`hmac.secret` |string |{nbsp} |Helper method to configure a password-like secret (instead of byte based #hmacSecret(byte[]). - The password is transformed to bytes with StandardCharsets#UTF_8 charset. +|`hmac.secret` |string |{nbsp} |Helper method to configure a password-like secret (instead of byte based hmacSecret(byte[]). + The password is transformed to bytes with StandardCharsets.UTF_8 charset. |`key-id` |string |{nbsp} |The key id of this client to map to this signature validation configuration. |`principal-name` |string |{nbsp} |The principal name of the client, defaults to keyId if not configured. |`principal-type` |SubjectType (USER, SERVICE) |`SERVICE` |The type of principal we have authenticated (either user or service, defaults to service). diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperProvider.adoc index 86ef810e6d0..3edcf211006 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsMtRoleMapperProvider.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -57,18 +57,18 @@ This type provides the following service implementations: |`cache-config` |xref:{rootdir}/config/io_helidon_security_providers_common_EvictableCache.adoc[EvictableCache] |{nbsp} |Use explicit io.helidon.security.providers.common.EvictableCache for role caching. |`default-idcs-subject-type` |string |`user` |Configure subject type to use when requesting roles from IDCS. - Can be either #IDCS_SUBJECT_TYPE_USER or #IDCS_SUBJECT_TYPE_CLIENT. - Defaults to #IDCS_SUBJECT_TYPE_USER. + Can be either IDCS_SUBJECT_TYPE_USER or IDCS_SUBJECT_TYPE_CLIENT. + Defaults to IDCS_SUBJECT_TYPE_USER. |`idcs-app-name-handler` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |Configure token handler for IDCS Application name. - By default the header `IdcsMtRoleMapperProvider#IDCS_APP_HEADER` is used. + By default the header IdcsMtRoleMapperProvider.IDCS_APP_HEADER is used. |`idcs-tenant-handler` |xref:{rootdir}/config/io_helidon_security_util_TokenHandler.adoc[TokenHandler] |{nbsp} |Configure token handler for IDCS Tenant ID. - By default the header `IdcsMtRoleMapperProvider#IDCS_TENANT_HEADER` is used. + By default the header IdcsMtRoleMapperProvider.IDCS_TENANT_HEADER is used. |`oidc-config` |xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig] |{nbsp} |Use explicit io.helidon.security.providers.oidc.common.OidcConfig instance, e.g. when using it also for OIDC provider. |`subject-types` |SubjectType[] (USER, SERVICE) |`USER` |Add a supported subject type. - If none added, io.helidon.security.SubjectType#USER is used. + If none added, io.helidon.security.SubjectType.USER is used. If any added, only the ones added will be used (e.g. if you want to use - both io.helidon.security.SubjectType#USER and io.helidon.security.SubjectType#SERVICE, + both io.helidon.security.SubjectType.USER and io.helidon.security.SubjectType.SERVICE, both need to be added. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProvider.adoc index 9d6b11a25b4..67b7f64bbc3 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProvider.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -57,14 +57,14 @@ This type provides the following service implementations: |`cache-config` |xref:{rootdir}/config/io_helidon_security_providers_common_EvictableCache.adoc[EvictableCache] |{nbsp} |Use explicit io.helidon.security.providers.common.EvictableCache for role caching. |`default-idcs-subject-type` |string |`user` |Configure subject type to use when requesting roles from IDCS. - Can be either #IDCS_SUBJECT_TYPE_USER or #IDCS_SUBJECT_TYPE_CLIENT. - Defaults to #IDCS_SUBJECT_TYPE_USER. + Can be either IDCS_SUBJECT_TYPE_USER or IDCS_SUBJECT_TYPE_CLIENT. + Defaults to IDCS_SUBJECT_TYPE_USER. |`oidc-config` |xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig] |{nbsp} |Use explicit io.helidon.security.providers.oidc.common.OidcConfig instance, e.g. when using it also for OIDC provider. |`subject-types` |SubjectType[] (USER, SERVICE) |`USER` |Add a supported subject type. - If none added, io.helidon.security.SubjectType#USER is used. + If none added, io.helidon.security.SubjectType.USER is used. If any added, only the ones added will be used (e.g. if you want to use - both io.helidon.security.SubjectType#USER and io.helidon.security.SubjectType#SERVICE, + both io.helidon.security.SubjectType.USER and io.helidon.security.SubjectType.SERVICE, both need to be added. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProviderBase_Builder.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProviderBase_Builder.adoc index c9bad6d6241..18f2c61476e 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProviderBase_Builder.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_idcs_mapper_IdcsRoleMapperProviderBase_Builder.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,14 +43,14 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.idcs.mapper.IdcsRole |key |type |default value |description |`default-idcs-subject-type` |string |`user` |Configure subject type to use when requesting roles from IDCS. - Can be either #IDCS_SUBJECT_TYPE_USER or #IDCS_SUBJECT_TYPE_CLIENT. - Defaults to #IDCS_SUBJECT_TYPE_USER. + Can be either IDCS_SUBJECT_TYPE_USER or IDCS_SUBJECT_TYPE_CLIENT. + Defaults to IDCS_SUBJECT_TYPE_USER. |`oidc-config` |xref:{rootdir}/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc[OidcConfig] |{nbsp} |Use explicit io.helidon.security.providers.oidc.common.OidcConfig instance, e.g. when using it also for OIDC provider. |`subject-types` |SubjectType[] (USER, SERVICE) |`USER` |Add a supported subject type. - If none added, io.helidon.security.SubjectType#USER is used. + If none added, io.helidon.security.SubjectType.USER is used. If any added, only the ones added will be used (e.g. if you want to use - both io.helidon.security.SubjectType#USER and io.helidon.security.SubjectType#SERVICE, + both io.helidon.security.SubjectType.USER and io.helidon.security.SubjectType.SERVICE, both need to be added. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_jwt_JwtProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_jwt_JwtProvider.adoc index 4941127f4f5..0a69c95ecdc 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_jwt_JwtProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_jwt_JwtProvider.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -56,7 +56,7 @@ This type provides the following service implementations: |key |type |default value |description |`allow-impersonation` |boolean |`false` |Whether to allow impersonation by explicitly overriding - username from outbound requests using io.helidon.security.EndpointConfig#PROPERTY_OUTBOUND_ID + username from outbound requests using io.helidon.security.EndpointConfig.PROPERTY_OUTBOUND_ID property. By default this is not allowed and identity can only be propagated. |`allow-unsigned` |boolean |`false` |Configure support for unsigned JWT. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_OidcProvider.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_OidcProvider.adoc index c48756d7273..bdf45263c19 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_OidcProvider.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_OidcProvider.adoc @@ -55,13 +55,15 @@ This type provides the following service implementations: |=== |key |type |default value |description +|`access-token-ip-check` |boolean |`true` |Whether to check if current IP address matches the one access token was issued for. + This check helps with cookie replay attack prevention. |`audience` |string |{nbsp} |Audience of issued tokens. |`authorization-endpoint-uri` |URI |{nbsp} |URI of an authorization endpoint used to redirect users to for logging-in. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/authorize. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/authorize. |`base-scopes` |string |`openid` |Configure base scopes. - By default, this is `DEFAULT_BASE_SCOPES`. + By default, this is DEFAULT_BASE_SCOPES. If scope has a qualifier, it must be used here. |`check-audience` |boolean |`false` |Configure audience claim check. |`client-id` |string |{nbsp} |Client ID as generated by OIDC server. @@ -73,35 +75,39 @@ This type provides the following service implementations: Not used by default. |`cookie-encryption-enabled` |boolean |`false` |Whether to encrypt token cookie created by this microservice. Defaults to `false`. -|`cookie-encryption-enabled-id-token` |boolean |`true` |Whether to encrypt id token cookie created by this microservice. +|`cookie-encryption-id-enabled` |boolean |`true` |Whether to encrypt id token cookie created by this microservice. Defaults to `true`. -|`cookie-encryption-enabled-refresh-token` |boolean |`true` |Whether to encrypt refresh token cookie created by this microservice. - Defaults to `true`. -|`cookie-encryption-enabled-tenant-name` |boolean |`true` |Whether to encrypt tenant name cookie created by this microservice. - Defaults to `true`. -|`cookie-encryption-name` |string |{nbsp} |Name of the encryption configuration available through Security#encrypt(String, byte[]) and - Security#decrypt(String, String). +|`cookie-encryption-name` |string |{nbsp} |Name of the encryption configuration available through Security.encrypt(String, byte[]) and + Security.decrypt(String, String). If configured and encryption is enabled for any cookie, Security MUST be configured in global or current `io.helidon.common.context.Context` (this is done automatically in Helidon MP). |`cookie-encryption-password` |char[] |{nbsp} |Master password for encryption/decryption of cookies. This must be configured to the same value on each microservice using the cookie. +|`cookie-encryption-refresh-enabled` |boolean |`true` |Whether to encrypt refresh token cookie created by this microservice. + Defaults to `true`. +|`cookie-encryption-state-enabled` |boolean |`true` |Whether to encrypt state cookie created by this microservice. + Defaults to `true`. +|`cookie-encryption-tenant-enabled` |boolean |`true` |Whether to encrypt tenant name cookie created by this microservice. + Defaults to `true`. |`cookie-http-only` |boolean |`true` |When using cookie, if set to true, the HttpOnly attribute will be configured. - Defaults to `OidcCookieHandler.Builder#DEFAULT_HTTP_ONLY`. + Defaults to OidcCookieHandler.Builder.DEFAULT_HTTP_ONLY. |`cookie-max-age-seconds` |long |{nbsp} |When using cookie, used to set MaxAge attribute of the cookie, defining how long the cookie is valid. Not used by default. |`cookie-name` |string |`JSESSIONID` |Name of the cookie to use. - Defaults to `DEFAULT_COOKIE_NAME`. + Defaults to DEFAULT_COOKIE_NAME. |`cookie-name-id-token` |string |`JSESSIONID_2` |Name of the cookie to use for id token. - Defaults to `DEFAULT_COOKIE_NAME`_2. + Defaults to DEFAULT_COOKIE_NAME_2. This cookie is only used when logout is enabled, as otherwise it is not needed. Content of this cookie is encrypted. |`cookie-name-refresh-token` |string |`JSESSIONID_3` |The name of the cookie to use for the refresh token. - Defaults to `DEFAULT_REFRESH_COOKIE_NAME`. + Defaults to DEFAULT_REFRESH_COOKIE_NAME. +|`cookie-name-state` |string |`JSESSIONID_3` |The name of the cookie to use for the state storage. + Defaults to DEFAULT_STATE_COOKIE_NAME. |`cookie-name-tenant` |string |`HELIDON_TENANT` |The name of the cookie to use for the tenant name. - Defaults to `DEFAULT_TENANT_COOKIE_NAME`. + Defaults to DEFAULT_TENANT_COOKIE_NAME. |`cookie-path` |string |`/` |Path the cookie is valid for. Defaults to "/". |`cookie-same-site` |SameSite (LAX, STRICT, NONE) |`LAX` |When using cookie, used to set the SameSite cookie value. Can be @@ -109,7 +115,7 @@ This type provides the following service implementations: |`cookie-secure` |boolean |`false` |When using cookie, if set to true, the Secure attribute will be configured. Defaults to false. |`cookie-use` |boolean |`true` |Whether to use cookie to store JWT between requests. - Defaults to `DEFAULT_COOKIE_USE`. + Defaults to DEFAULT_COOKIE_USE. |`cors` |xref:{rootdir}/config/io_helidon_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assign cross-origin resource sharing settings. |`force-https-redirects` |boolean |`false` |Force HTTPS for redirects to identity provider. Defaults to `false`. @@ -124,12 +130,12 @@ This type provides the following service implementations: Change this setting only when you really know what you are doing, otherwise it could case security issues. |`identity-uri` |URI |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata. |`introspect-endpoint-uri` |URI |{nbsp} |Endpoint to use to validate JWT. - Either use this or set #signJwk(JwkKeys) or #signJwk(Resource). + Either use this or set signJwk(JwkKeys) or signJwk(Resource). |`issuer` |string |{nbsp} |Issuer of issued tokens. |`max-redirects` |int |`5` |Configure maximal number of redirects when redirecting to an OIDC provider within a single authentication attempt. - Defaults to `DEFAULT_MAX_REDIRECTS` + Defaults to DEFAULT_MAX_REDIRECTS |`oidc-metadata-well-known` |boolean |`true` |If set to true, metadata will be loaded from default (well known) location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded even if oidc-metadata-resource is not defined. In such a case all URIs must be explicitly defined (e.g. @@ -145,13 +151,13 @@ This type provides the following service implementations: |`proxy-host` |string |{nbsp} |Proxy host to use. When defined, triggers usage of proxy for HTTP requests. Setting to empty String has the same meaning as setting to null - disables proxy. |`proxy-port` |int |`80` |Proxy port. - Defaults to `DEFAULT_PROXY_PORT` + Defaults to DEFAULT_PROXY_PORT |`proxy-protocol` |string |`http` |Proxy protocol to use when proxy is used. - Defaults to `DEFAULT_PROXY_PROTOCOL`. + Defaults to DEFAULT_PROXY_PROTOCOL. |`query-id-token-param-name` |string |`id_token` |Name of a query parameter that contains the JWT id token when parameter is used. |`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT access token when parameter is used. |`query-param-tenant-name` |string |`h_tenant` |Name of a query parameter that contains the tenant name when the parameter is used. - Defaults to #DEFAULT_TENANT_PARAM_NAME. + Defaults to DEFAULT_TENANT_PARAM_NAME. |`query-param-use` |boolean |`false` |Whether to use a query parameter to send JWT token from application to this server. |`redirect` |boolean |`false` |By default, the client should redirect to the identity server for the user to log in. @@ -159,19 +165,19 @@ This type provides the following service implementations: will not redirect and just return appropriate error response code. |`redirect-attempt-param` |string |`h_ra` |Configure the parameter used to store the number of attempts in redirect. - Defaults to `DEFAULT_ATTEMPT_PARAM` + Defaults to DEFAULT_ATTEMPT_PARAM |`redirect-uri` |string |`/oidc/redirect` |URI to register web server component on, used by the OIDC server to redirect authorization requests to after a user logs in or approves scopes. Note that usually the redirect URI configured here must be the same one as configured on OIDC server. - Defaults to `DEFAULT_REDIRECT_URI` + Defaults to DEFAULT_REDIRECT_URI |`relative-uris` |boolean |`false` |Can be set to `true` to force the use of relative URIs in all requests, regardless of the presence or absence of proxies or no-proxy lists. By default, requests that use the Proxy will have absolute URIs. Set this flag to `true` if the host is unable to accept absolute URIs. - Defaults to `DEFAULT_RELATIVE_URIS`. + Defaults to DEFAULT_RELATIVE_URIS. |`scope-audience` |string |{nbsp} |Audience of the scope required by this application. This is prefixed to the scope name when requesting scopes from the identity server. Defaults to empty string. @@ -184,15 +190,15 @@ This type provides the following service implementations: |`token-endpoint-auth` |ClientAuthentication (CLIENT_SECRET_BASIC, CLIENT_SECRET_POST, CLIENT_SECRET_JWT, PRIVATE_KEY_JWT, NONE) |`CLIENT_SECRET_BASIC` |Type of authentication to use when invoking the token endpoint. Current supported options: -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_BASIC -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_POST -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#NONE +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_BASIC +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_POST +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.NONE |`token-endpoint-uri` |URI |{nbsp} |URI of a token endpoint used to obtain a JWT based on the authentication code. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/token. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/token. |`token-signature-validation` |boolean |`true` |Whether access token signature check should be enabled. Signature check is enabled by default, and it is highly recommended to not change that. Change this setting only when you really know what you are doing, otherwise it could case security issues. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_BaseBuilder.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_BaseBuilder.adoc index fe433a3f0fe..0d030dac053 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_BaseBuilder.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_BaseBuilder.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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,10 +45,10 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`audience` |string |{nbsp} |Audience of issued tokens. |`authorization-endpoint-uri` |URI |{nbsp} |URI of an authorization endpoint used to redirect users to for logging-in. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/authorize. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/authorize. |`base-scopes` |string |`openid` |Configure base scopes. - By default, this is `DEFAULT_BASE_SCOPES`. + By default, this is DEFAULT_BASE_SCOPES. If scope has a qualifier, it must be used here. |`check-audience` |boolean |`false` |Configure audience claim check. |`client-id` |string |{nbsp} |Client ID as generated by OIDC server. @@ -58,7 +58,7 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`client-timeout-millis` |Duration |`30000` |Timeout of calls using web client. |`identity-uri` |URI |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata. |`introspect-endpoint-uri` |URI |{nbsp} |Endpoint to use to validate JWT. - Either use this or set #signJwk(JwkKeys) or #signJwk(Resource). + Either use this or set signJwk(JwkKeys) or signJwk(Resource). |`issuer` |string |{nbsp} |Issuer of issued tokens. |`oidc-metadata-well-known` |boolean |`true` |If set to true, metadata will be loaded from default (well known) location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded @@ -78,15 +78,15 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`token-endpoint-auth` |ClientAuthentication (CLIENT_SECRET_BASIC, CLIENT_SECRET_POST, CLIENT_SECRET_JWT, PRIVATE_KEY_JWT, NONE) |`CLIENT_SECRET_BASIC` |Type of authentication to use when invoking the token endpoint. Current supported options: -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_BASIC -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_POST -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#NONE +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_BASIC +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_POST +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.NONE |`token-endpoint-uri` |URI |{nbsp} |URI of a token endpoint used to obtain a JWT based on the authentication code. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/token. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/token. |`validate-jwt-with-jwk` |boolean |`true` |Use JWK (a set of keys to validate signatures of JWT) to validate tokens. Use this method when you want to use default values for JWK or introspection endpoint URI. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc index 39f177902af..2b2f8552905 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc @@ -44,13 +44,15 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |=== |key |type |default value |description +|`access-token-ip-check` |boolean |`true` |Whether to check if current IP address matches the one access token was issued for. + This check helps with cookie replay attack prevention. |`audience` |string |{nbsp} |Audience of issued tokens. |`authorization-endpoint-uri` |URI |{nbsp} |URI of an authorization endpoint used to redirect users to for logging-in. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/authorize. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/authorize. |`base-scopes` |string |`openid` |Configure base scopes. - By default, this is `DEFAULT_BASE_SCOPES`. + By default, this is DEFAULT_BASE_SCOPES. If scope has a qualifier, it must be used here. |`check-audience` |boolean |`false` |Configure audience claim check. |`client-id` |string |{nbsp} |Client ID as generated by OIDC server. @@ -62,35 +64,39 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid Not used by default. |`cookie-encryption-enabled` |boolean |`false` |Whether to encrypt token cookie created by this microservice. Defaults to `false`. -|`cookie-encryption-enabled-id-token` |boolean |`true` |Whether to encrypt id token cookie created by this microservice. +|`cookie-encryption-id-enabled` |boolean |`true` |Whether to encrypt id token cookie created by this microservice. Defaults to `true`. -|`cookie-encryption-enabled-refresh-token` |boolean |`true` |Whether to encrypt refresh token cookie created by this microservice. - Defaults to `true`. -|`cookie-encryption-enabled-tenant-name` |boolean |`true` |Whether to encrypt tenant name cookie created by this microservice. - Defaults to `true`. -|`cookie-encryption-name` |string |{nbsp} |Name of the encryption configuration available through Security#encrypt(String, byte[]) and - Security#decrypt(String, String). +|`cookie-encryption-name` |string |{nbsp} |Name of the encryption configuration available through Security.encrypt(String, byte[]) and + Security.decrypt(String, String). If configured and encryption is enabled for any cookie, Security MUST be configured in global or current `io.helidon.common.context.Context` (this is done automatically in Helidon MP). |`cookie-encryption-password` |char[] |{nbsp} |Master password for encryption/decryption of cookies. This must be configured to the same value on each microservice using the cookie. +|`cookie-encryption-refresh-enabled` |boolean |`true` |Whether to encrypt refresh token cookie created by this microservice. + Defaults to `true`. +|`cookie-encryption-state-enabled` |boolean |`true` |Whether to encrypt state cookie created by this microservice. + Defaults to `true`. +|`cookie-encryption-tenant-enabled` |boolean |`true` |Whether to encrypt tenant name cookie created by this microservice. + Defaults to `true`. |`cookie-http-only` |boolean |`true` |When using cookie, if set to true, the HttpOnly attribute will be configured. - Defaults to `OidcCookieHandler.Builder#DEFAULT_HTTP_ONLY`. + Defaults to OidcCookieHandler.Builder.DEFAULT_HTTP_ONLY. |`cookie-max-age-seconds` |long |{nbsp} |When using cookie, used to set MaxAge attribute of the cookie, defining how long the cookie is valid. Not used by default. |`cookie-name` |string |`JSESSIONID` |Name of the cookie to use. - Defaults to `DEFAULT_COOKIE_NAME`. + Defaults to DEFAULT_COOKIE_NAME. |`cookie-name-id-token` |string |`JSESSIONID_2` |Name of the cookie to use for id token. - Defaults to `DEFAULT_COOKIE_NAME`_2. + Defaults to DEFAULT_COOKIE_NAME_2. This cookie is only used when logout is enabled, as otherwise it is not needed. Content of this cookie is encrypted. |`cookie-name-refresh-token` |string |`JSESSIONID_3` |The name of the cookie to use for the refresh token. - Defaults to `DEFAULT_REFRESH_COOKIE_NAME`. + Defaults to DEFAULT_REFRESH_COOKIE_NAME. +|`cookie-name-state` |string |`JSESSIONID_3` |The name of the cookie to use for the state storage. + Defaults to DEFAULT_STATE_COOKIE_NAME. |`cookie-name-tenant` |string |`HELIDON_TENANT` |The name of the cookie to use for the tenant name. - Defaults to `DEFAULT_TENANT_COOKIE_NAME`. + Defaults to DEFAULT_TENANT_COOKIE_NAME. |`cookie-path` |string |`/` |Path the cookie is valid for. Defaults to "/". |`cookie-same-site` |SameSite (LAX, STRICT, NONE) |`LAX` |When using cookie, used to set the SameSite cookie value. Can be @@ -98,7 +104,7 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`cookie-secure` |boolean |`false` |When using cookie, if set to true, the Secure attribute will be configured. Defaults to false. |`cookie-use` |boolean |`true` |Whether to use cookie to store JWT between requests. - Defaults to `DEFAULT_COOKIE_USE`. + Defaults to DEFAULT_COOKIE_USE. |`cors` |xref:{rootdir}/config/io_helidon_cors_CrossOriginConfig.adoc[CrossOriginConfig] |{nbsp} |Assign cross-origin resource sharing settings. |`force-https-redirects` |boolean |`false` |Force HTTPS for redirects to identity provider. Defaults to `false`. @@ -113,12 +119,12 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid Change this setting only when you really know what you are doing, otherwise it could case security issues. |`identity-uri` |URI |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata. |`introspect-endpoint-uri` |URI |{nbsp} |Endpoint to use to validate JWT. - Either use this or set #signJwk(JwkKeys) or #signJwk(Resource). + Either use this or set signJwk(JwkKeys) or signJwk(Resource). |`issuer` |string |{nbsp} |Issuer of issued tokens. |`max-redirects` |int |`5` |Configure maximal number of redirects when redirecting to an OIDC provider within a single authentication attempt. - Defaults to `DEFAULT_MAX_REDIRECTS` + Defaults to DEFAULT_MAX_REDIRECTS |`oidc-metadata-well-known` |boolean |`true` |If set to true, metadata will be loaded from default (well known) location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded even if oidc-metadata-resource is not defined. In such a case all URIs must be explicitly defined (e.g. @@ -129,13 +135,13 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`proxy-host` |string |{nbsp} |Proxy host to use. When defined, triggers usage of proxy for HTTP requests. Setting to empty String has the same meaning as setting to null - disables proxy. |`proxy-port` |int |`80` |Proxy port. - Defaults to `DEFAULT_PROXY_PORT` + Defaults to DEFAULT_PROXY_PORT |`proxy-protocol` |string |`http` |Proxy protocol to use when proxy is used. - Defaults to `DEFAULT_PROXY_PROTOCOL`. + Defaults to DEFAULT_PROXY_PROTOCOL. |`query-id-token-param-name` |string |`id_token` |Name of a query parameter that contains the JWT id token when parameter is used. |`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT access token when parameter is used. |`query-param-tenant-name` |string |`h_tenant` |Name of a query parameter that contains the tenant name when the parameter is used. - Defaults to #DEFAULT_TENANT_PARAM_NAME. + Defaults to DEFAULT_TENANT_PARAM_NAME. |`query-param-use` |boolean |`false` |Whether to use a query parameter to send JWT token from application to this server. |`redirect` |boolean |`false` |By default, the client should redirect to the identity server for the user to log in. @@ -143,19 +149,19 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid will not redirect and just return appropriate error response code. |`redirect-attempt-param` |string |`h_ra` |Configure the parameter used to store the number of attempts in redirect. - Defaults to `DEFAULT_ATTEMPT_PARAM` + Defaults to DEFAULT_ATTEMPT_PARAM |`redirect-uri` |string |`/oidc/redirect` |URI to register web server component on, used by the OIDC server to redirect authorization requests to after a user logs in or approves scopes. Note that usually the redirect URI configured here must be the same one as configured on OIDC server. - Defaults to `DEFAULT_REDIRECT_URI` + Defaults to DEFAULT_REDIRECT_URI |`relative-uris` |boolean |`false` |Can be set to `true` to force the use of relative URIs in all requests, regardless of the presence or absence of proxies or no-proxy lists. By default, requests that use the Proxy will have absolute URIs. Set this flag to `true` if the host is unable to accept absolute URIs. - Defaults to `DEFAULT_RELATIVE_URIS`. + Defaults to DEFAULT_RELATIVE_URIS. |`scope-audience` |string |{nbsp} |Audience of the scope required by this application. This is prefixed to the scope name when requesting scopes from the identity server. Defaults to empty string. @@ -168,15 +174,15 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`token-endpoint-auth` |ClientAuthentication (CLIENT_SECRET_BASIC, CLIENT_SECRET_POST, CLIENT_SECRET_JWT, PRIVATE_KEY_JWT, NONE) |`CLIENT_SECRET_BASIC` |Type of authentication to use when invoking the token endpoint. Current supported options: -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_BASIC -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_POST -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#NONE +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_BASIC +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_POST +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.NONE |`token-endpoint-uri` |URI |{nbsp} |URI of a token endpoint used to obtain a JWT based on the authentication code. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/token. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/token. |`token-signature-validation` |boolean |`true` |Whether access token signature check should be enabled. Signature check is enabled by default, and it is highly recommended to not change that. Change this setting only when you really know what you are doing, otherwise it could case security issues. diff --git a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_TenantConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_TenantConfig.adoc index 0d5e3464847..633a3eeac59 100644 --- a/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_TenantConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_TenantConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -56,10 +56,10 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`audience` |string |{nbsp} |Audience of issued tokens. |`authorization-endpoint-uri` |URI |{nbsp} |URI of an authorization endpoint used to redirect users to for logging-in. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/authorize. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/authorize. |`base-scopes` |string |`openid` |Configure base scopes. - By default, this is `DEFAULT_BASE_SCOPES`. + By default, this is DEFAULT_BASE_SCOPES. If scope has a qualifier, it must be used here. |`check-audience` |boolean |`false` |Configure audience claim check. |`client-id` |string |{nbsp} |Client ID as generated by OIDC server. @@ -69,7 +69,7 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`client-timeout-millis` |Duration |`30000` |Timeout of calls using web client. |`identity-uri` |URI |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata. |`introspect-endpoint-uri` |URI |{nbsp} |Endpoint to use to validate JWT. - Either use this or set #signJwk(JwkKeys) or #signJwk(Resource). + Either use this or set signJwk(JwkKeys) or signJwk(Resource). |`issuer` |string |{nbsp} |Issuer of issued tokens. |`oidc-metadata-well-known` |boolean |`true` |If set to true, metadata will be loaded from default (well known) location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded @@ -89,15 +89,15 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid |`token-endpoint-auth` |ClientAuthentication (CLIENT_SECRET_BASIC, CLIENT_SECRET_POST, CLIENT_SECRET_JWT, PRIVATE_KEY_JWT, NONE) |`CLIENT_SECRET_BASIC` |Type of authentication to use when invoking the token endpoint. Current supported options: -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_BASIC -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#CLIENT_SECRET_POST -- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication#NONE +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_BASIC +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.CLIENT_SECRET_POST +- io.helidon.security.providers.oidc.common.OidcConfig.ClientAuthentication.NONE |`token-endpoint-uri` |URI |{nbsp} |URI of a token endpoint used to obtain a JWT based on the authentication code. - If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined - an attempt is made to use #identityUri(URI)/oauth2/v1/token. + If not defined, it is obtained from oidcMetadata(Resource), if that is not defined + an attempt is made to use identityUri(URI)/oauth2/v1/token. |`validate-jwt-with-jwk` |boolean |`true` |Use JWK (a set of keys to validate signatures of JWT) to validate tokens. Use this method when you want to use default values for JWK or introspection endpoint URI. diff --git a/docs/src/main/asciidoc/config/io_helidon_tracing_providers_zipkin_ZipkinTracerBuilder.adoc b/docs/src/main/asciidoc/config/io_helidon_tracing_providers_zipkin_ZipkinTracerBuilder.adoc index 93beb75f9eb..f8e347a05fa 100644 --- a/docs/src/main/asciidoc/config/io_helidon_tracing_providers_zipkin_ZipkinTracerBuilder.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_tracing_providers_zipkin_ZipkinTracerBuilder.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -47,7 +47,7 @@ This is a standalone configuration type, prefix from configuration root: `tracin |key |type |default value |description |`api-version` |Version (V1, V2) |`V2` |Version of Zipkin API to use. - Defaults to Version#V2. + Defaults to Version.V2. |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpClientConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpClientConfig.adoc index 14be35ffabb..245364d250c 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpClientConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpClientConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -42,12 +42,12 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.api/io/helidon/webclient/api/ |=== |key |type |default value |description -|`base-uri` |string |{nbsp} |Base uri used by the client in all requests. +|`base-uri` |ClientUri |{nbsp} |Base uri used by the client in all requests. - @return base uri of the client requests + Base uri of the client requests |`connect-timeout` |Duration |{nbsp} |Connect timeout. - @return connect timeout + Connect timeout @see io.helidon.common.socket.SocketOptions#connectTimeout() |`connection-cache-size` |int |`256` |Maximal size of the connection cache. For most HTTP protocols, we may cache connections to various endpoints for keep alive (or stream reuse in case of HTTP/2). @@ -57,20 +57,20 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.api/io/helidon/webclient/api/ This method discards all previously registered ContentEncodingContext. If no content encoding context is registered, default encoding context is used. - @return content encoding context + Content encoding context |`cookie-manager` |xref:{rootdir}/config/io_helidon_webclient_api_WebClientCookieManager.adoc[WebClientCookieManager] |{nbsp} |WebClient cookie manager. - @return cookie manager to use + Cookie manager to use |`default-headers` |Map<string, string> |{nbsp} |Default headers to be used in every request from configuration. - @return default headers + Default headers |`follow-redirects` |boolean |`true` |Whether to follow redirects. - @return whether to follow redirects + Whether to follow redirects |`keep-alive` |boolean |`true` |Determines if connection keep alive is enabled (NOT socket keep alive, but HTTP connection keep alive, to re-use the same connection for multiple requests). - @return keep alive for this connection + Keep alive for this connection @see io.helidon.common.socket.SocketOptions#socketKeepAlive() |`max-in-memory-entity` |int |`131072` |If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance. If bigger, streaming will be used. @@ -78,63 +78,63 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.api/io/helidon/webclient/api/ Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such cases, this option is ignored. Default is 128Kb. - @return maximal number of bytes to buffer in memory for supported writers + Maximal number of bytes to buffer in memory for supported writers |`max-redirects` |int |`10` |Max number of followed redirects. - This is ignored if #followRedirects() option is `false`. + This is ignored if followRedirects() option is `false`. - @return max number of followed redirects + Max number of followed redirects |`media-context` |xref:{rootdir}/config/io_helidon_http_media_MediaContext.adoc[MediaContext] |`create()` |Configure the listener specific io.helidon.http.media.MediaContext. This method discards all previously registered MediaContext. If no media context is registered, default media context is used. - @return media context -|`media-type-parser-mode` |ParserMode (STRICT, RELAXED) |`STRICT` |Configure media type parsing mode for HTTP `Content-Type` header. + Media context +|`media-type-parser-mode` |ParserMode |`ParserMode.STRICT` |Configure media type parsing mode for HTTP `Content-Type` header. - @return media type parsing mode + Media type parsing mode |`properties` |Map<string, string> |{nbsp} |Properties configured for this client. These properties are propagated through client request, to be used by services (and possibly for other purposes). - @return map of client properties + Map of client properties |`proxy` |xref:{rootdir}/config/io_helidon_webclient_api_Proxy.adoc[Proxy] |{nbsp} |Proxy configuration to be used for requests. - @return proxy to use, defaults to Proxy#noProxy() + Proxy to use, defaults to Proxy.noProxy() |`read-continue-timeout` |Duration |`PT1S` |Socket 100-Continue read timeout. Default is 1 second. This read timeout is used when 100-Continue is sent by the client, before it sends an entity. - @return read 100-Continue timeout duration + Read 100-Continue timeout duration |`read-timeout` |Duration |{nbsp} |Read timeout. - @return read timeout + Read timeout @see io.helidon.common.socket.SocketOptions#readTimeout() |`relative-uris` |boolean |`false` |Can be set to `true` to force the use of relative URIs in all requests, regardless of the presence or absence of proxies or no-proxy lists. - @return relative URIs flag + Relative URIs flag |`send-expect-continue` |boolean |`true` |Whether Expect-100-Continue header is sent to verify server availability before sending an entity. Defaults to `true`. - @return whether Expect:100-Continue header should be sent on streamed transfers + Whether Expect:100-Continue header should be sent on streamed transfers |`services` |io.helidon.webclient.spi.WebClientService[] (service provider interface) |{nbsp} |WebClient services. - @return services to use with this web client + Services to use with this web client |`share-connection-cache` |boolean |`true` |Whether to share connection cache between all the WebClient instances in JVM. - @return true if connection cache is shared + True if connection cache is shared |`socket-options` |xref:{rootdir}/config/io_helidon_common_socket_SocketOptions.adoc[SocketOptions] |{nbsp} |Socket options for connections opened by this client. If there is a value explicitly configured on this type and on the socket options, the one configured on this type's builder will win: -- #readTimeout() -- #connectTimeout() +- readTimeout() +- connectTimeout() -@return socket options +Socket options |`tls` |xref:{rootdir}/config/io_helidon_common_tls_Tls.adoc[Tls] |{nbsp} |TLS configuration for any TLS request from this client. TLS can also be configured per request. TLS is used when the protocol is set to `https`. - @return TLS configuration to use + TLS configuration to use |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpConfigBase.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpConfigBase.adoc index 02b72851517..2effa290f5c 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpConfigBase.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_api_HttpConfigBase.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,36 +44,36 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.api/io/helidon/webclient/api/ |`connect-timeout` |Duration |{nbsp} |Connect timeout. - @return connect timeout + Connect timeout @see io.helidon.common.socket.SocketOptions#connectTimeout() |`follow-redirects` |boolean |`true` |Whether to follow redirects. - @return whether to follow redirects + Whether to follow redirects |`keep-alive` |boolean |`true` |Determines if connection keep alive is enabled (NOT socket keep alive, but HTTP connection keep alive, to re-use the same connection for multiple requests). - @return keep alive for this connection + Keep alive for this connection @see io.helidon.common.socket.SocketOptions#socketKeepAlive() |`max-redirects` |int |`10` |Max number of followed redirects. - This is ignored if #followRedirects() option is `false`. + This is ignored if followRedirects() option is `false`. - @return max number of followed redirects + Max number of followed redirects |`properties` |Map<string, string> |{nbsp} |Properties configured for this client. These properties are propagated through client request, to be used by services (and possibly for other purposes). - @return map of client properties + Map of client properties |`proxy` |xref:{rootdir}/config/io_helidon_webclient_api_Proxy.adoc[Proxy] |{nbsp} |Proxy configuration to be used for requests. - @return proxy to use, defaults to Proxy#noProxy() + Proxy to use, defaults to Proxy.noProxy() |`read-timeout` |Duration |{nbsp} |Read timeout. - @return read timeout + Read timeout @see io.helidon.common.socket.SocketOptions#readTimeout() |`tls` |xref:{rootdir}/config/io_helidon_common_tls_Tls.adoc[Tls] |{nbsp} |TLS configuration for any TLS request from this client. TLS can also be configured per request. TLS is used when the protocol is set to `https`. - @return TLS configuration to use + TLS configuration to use |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClient.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClient.adoc index eff62260c3e..36d9a35edcd 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClient.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClient.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,12 +44,12 @@ This is a standalone configuration type, prefix from configuration root: `client |=== |key |type |default value |description -|`base-uri` |string |{nbsp} |Base uri used by the client in all requests. +|`base-uri` |ClientUri |{nbsp} |Base uri used by the client in all requests. - @return base uri of the client requests + Base uri of the client requests |`connect-timeout` |Duration |{nbsp} |Connect timeout. - @return connect timeout + Connect timeout @see io.helidon.common.socket.SocketOptions#connectTimeout() |`connection-cache-size` |int |`256` |Maximal size of the connection cache. For most HTTP protocols, we may cache connections to various endpoints for keep alive (or stream reuse in case of HTTP/2). @@ -59,20 +59,20 @@ This is a standalone configuration type, prefix from configuration root: `client This method discards all previously registered ContentEncodingContext. If no content encoding context is registered, default encoding context is used. - @return content encoding context + Content encoding context |`cookie-manager` |xref:{rootdir}/config/io_helidon_webclient_api_WebClientCookieManager.adoc[WebClientCookieManager] |{nbsp} |WebClient cookie manager. - @return cookie manager to use + Cookie manager to use |`default-headers` |Map<string, string> |{nbsp} |Default headers to be used in every request from configuration. - @return default headers + Default headers |`follow-redirects` |boolean |`true` |Whether to follow redirects. - @return whether to follow redirects + Whether to follow redirects |`keep-alive` |boolean |`true` |Determines if connection keep alive is enabled (NOT socket keep alive, but HTTP connection keep alive, to re-use the same connection for multiple requests). - @return keep alive for this connection + Keep alive for this connection @see io.helidon.common.socket.SocketOptions#socketKeepAlive() |`max-in-memory-entity` |int |`131072` |If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance. If bigger, streaming will be used. @@ -80,66 +80,66 @@ This is a standalone configuration type, prefix from configuration root: `client Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such cases, this option is ignored. Default is 128Kb. - @return maximal number of bytes to buffer in memory for supported writers + Maximal number of bytes to buffer in memory for supported writers |`max-redirects` |int |`10` |Max number of followed redirects. - This is ignored if #followRedirects() option is `false`. + This is ignored if followRedirects() option is `false`. - @return max number of followed redirects + Max number of followed redirects |`media-context` |xref:{rootdir}/config/io_helidon_http_media_MediaContext.adoc[MediaContext] |`create()` |Configure the listener specific io.helidon.http.media.MediaContext. This method discards all previously registered MediaContext. If no media context is registered, default media context is used. - @return media context -|`media-type-parser-mode` |ParserMode (STRICT, RELAXED) |`STRICT` |Configure media type parsing mode for HTTP `Content-Type` header. + Media context +|`media-type-parser-mode` |ParserMode |`ParserMode.STRICT` |Configure media type parsing mode for HTTP `Content-Type` header. - @return media type parsing mode + Media type parsing mode |`properties` |Map<string, string> |{nbsp} |Properties configured for this client. These properties are propagated through client request, to be used by services (and possibly for other purposes). - @return map of client properties + Map of client properties |`protocol-configs` |io.helidon.webclient.spi.ProtocolConfig[] (service provider interface) |{nbsp} |Configuration of client protocols. - @return client protocol configurations + Client protocol configurations |`proxy` |xref:{rootdir}/config/io_helidon_webclient_api_Proxy.adoc[Proxy] |{nbsp} |Proxy configuration to be used for requests. - @return proxy to use, defaults to Proxy#noProxy() + Proxy to use, defaults to Proxy.noProxy() |`read-continue-timeout` |Duration |`PT1S` |Socket 100-Continue read timeout. Default is 1 second. This read timeout is used when 100-Continue is sent by the client, before it sends an entity. - @return read 100-Continue timeout duration + Read 100-Continue timeout duration |`read-timeout` |Duration |{nbsp} |Read timeout. - @return read timeout + Read timeout @see io.helidon.common.socket.SocketOptions#readTimeout() |`relative-uris` |boolean |`false` |Can be set to `true` to force the use of relative URIs in all requests, regardless of the presence or absence of proxies or no-proxy lists. - @return relative URIs flag + Relative URIs flag |`send-expect-continue` |boolean |`true` |Whether Expect-100-Continue header is sent to verify server availability before sending an entity. Defaults to `true`. - @return whether Expect:100-Continue header should be sent on streamed transfers + Whether Expect:100-Continue header should be sent on streamed transfers |`services` |io.helidon.webclient.spi.WebClientService[] (service provider interface) |{nbsp} |WebClient services. - @return services to use with this web client + Services to use with this web client |`share-connection-cache` |boolean |`true` |Whether to share connection cache between all the WebClient instances in JVM. - @return true if connection cache is shared + True if connection cache is shared |`socket-options` |xref:{rootdir}/config/io_helidon_common_socket_SocketOptions.adoc[SocketOptions] |{nbsp} |Socket options for connections opened by this client. If there is a value explicitly configured on this type and on the socket options, the one configured on this type's builder will win: -- #readTimeout() -- #connectTimeout() +- readTimeout() +- connectTimeout() -@return socket options +Socket options |`tls` |xref:{rootdir}/config/io_helidon_common_tls_Tls.adoc[Tls] |{nbsp} |TLS configuration for any TLS request from this client. TLS can also be configured per request. TLS is used when the protocol is set to `https`. - @return TLS configuration to use + TLS configuration to use |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClientCookieManager.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClientCookieManager.adoc index cfa6b1548e5..19bf5de5341 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClientCookieManager.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_api_WebClientCookieManager.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,13 +44,13 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.api/io/helidon/webclient/api/ |`automatic-store-enabled` |boolean |`false` |Whether automatic cookie store is enabled or not. - @return status of cookie store + Status of cookie store |`cookie-policy` |CookiePolicy |`java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER` |Current cookie policy for this client. - @return the cookie policy + The cookie policy |`default-cookies` |Map<string, string> |{nbsp} |Map of default cookies to include in all requests if cookies enabled. - @return map of default cookies + Map of default cookies |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClient.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClient.adoc new file mode 100644 index 00000000000..5fc378bd783 --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClient.adoc @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.webclient.grpc.GrpcClient +:keywords: helidon, config, io.helidon.webclient.grpc.GrpcClient +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.webclient.grpc.GrpcClient +include::{rootdir}/includes/attributes.adoc[] + += GrpcClient (webclient.grpc) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.webclient.grpc/io/helidon/webclient/grpc/GrpcClient.html[io.helidon.webclient.grpc.GrpcClient] + + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`protocol-config` |xref:{rootdir}/config/io_helidon_webclient_grpc_GrpcClientProtocolConfig.adoc[GrpcClientProtocolConfig] |`create()` |gRPC specific configuration. + + Protocol specific configuration + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClientProtocolConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClientProtocolConfig.adoc new file mode 100644 index 00000000000..ba69ecc4a4e --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_grpc_GrpcClientProtocolConfig.adoc @@ -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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.webclient.grpc.GrpcClientProtocolConfig +:keywords: helidon, config, io.helidon.webclient.grpc.GrpcClientProtocolConfig +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.webclient.grpc.GrpcClientProtocolConfig +include::{rootdir}/includes/attributes.adoc[] + += GrpcClientProtocolConfig (webclient.grpc) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.webclient.grpc/io/helidon/webclient/grpc/GrpcClientProtocolConfig.html[io.helidon.webclient.grpc.GrpcClientProtocolConfig] + + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`abort-poll-time-expired` |boolean |`false` |Whether to continue retrying after a poll wait timeout expired or not. If a read + operation timeouts out and this flag is set to `false`, the event is logged + and the client will retry. Otherwise, an exception is thrown. + + Abort timeout flag +|`heartbeat-period` |Duration |`PT0S` |How often to send a heartbeat (HTTP/2 ping) to check if the connection is still + alive. This is useful for long-running, streaming gRPC calls. It is turned off by + default but can be enabled by setting the period to a value greater than 0. + + Heartbeat period +|`init-buffer-size` |int |`2048` |Initial buffer size used to serialize gRPC request payloads. Buffers shall grow + according to the payload size, but setting this initial buffer size to a larger value + may improve performance for certain applications. + + Initial buffer size +|`name` |string |`grpc` |Name identifying this client protocol. Defaults to type. + + Name of client protocol +|`poll-wait-time` |Duration |`PT10S` |How long to wait for the next HTTP/2 data frame to arrive in underlying stream. + Whether this is a fatal error or not is controlled by abortPollTimeExpired(). + + Poll time as a duration + @see io.helidon.common.socket.SocketOptions#readTimeout() + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_http1_Http1ClientProtocolConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_http1_Http1ClientProtocolConfig.adoc index ca29f968b9f..8e05c9db49d 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_http1_Http1ClientProtocolConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_http1_Http1ClientProtocolConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,27 +44,27 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.http1/io/helidon/webclient/ht |`default-keep-alive` |boolean |`true` |Whether to use keep alive by default. - @return `true` for keeping connections alive and re-using them for multiple requests (default), `false` + `true` for keeping connections alive and re-using them for multiple requests (default), `false` to create a new connection for each request |`max-header-size` |int |`16384` |Configure the maximum allowed header size of the response. - @return maximum header size + maximum header size |`max-status-line-length` |int |`256` |Configure the maximum allowed length of the status line from the response. - @return maximum status line length + Maximum status line length |`name` |string |`http_1_1` | |`validate-request-headers` |boolean |`false` |Sets whether the request header format is validated or not. Defaults to `false` as user has control on the header creation. - @return whether request header validation should be enabled + Whether request header validation should be enabled |`validate-response-headers` |boolean |`true` |Sets whether the response header format is validated or not. Defaults to `true`. - @return whether response header validation should be enabled + Whether response header validation should be enabled |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_http2_Http2ClientProtocolConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_http2_Http2ClientProtocolConfig.adoc index fe676b23289..9e0e74061d0 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_http2_Http2ClientProtocolConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_http2_Http2ClientProtocolConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,31 +44,31 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.http2/io/helidon/webclient/ht |`flow-control-block-timeout` |Duration |`PT0.1S` |Timeout for blocking between windows size check iterations. - @return timeout + Timeout |`initial-window-size` |int |`65535` |Configure INITIAL_WINDOW_SIZE setting for new HTTP/2 connections. Sends to the server the size of the largest frame payload client is willing to receive. - Defaults to `io.helidon.http.http2.WindowSize#DEFAULT_WIN_SIZE`. + Defaults to io.helidon.http.http2.WindowSize.DEFAULT_WIN_SIZE. - @return units of octets + Units of octets |`max-frame-size` |int |`16384` |Configure initial MAX_FRAME_SIZE setting for new HTTP/2 connections. Maximum size of data frames in bytes the client is prepared to accept from the server. Default value is 2^14(16_384). - @return data frame size in bytes between 2^14(16_384) and 2^24-1(16_777_215) + Data frame size in bytes between 2^14(16_384) and 2^24-1(16_777_215) |`max-header-list-size` |long |`-1` |Configure initial MAX_HEADER_LIST_SIZE setting for new HTTP/2 connections. Sends to the server the maximum header field section size client is prepared to accept. Defaults to `-1`, which means "unconfigured". - @return units of octets + Units of octets |`name` |string |`h2` | |`ping` |boolean |`false` |Check healthiness of cached connections with HTTP/2.0 ping frame. Defaults to `false`. - @return use ping if true + Use ping if true |`ping-timeout` |Duration |`PT0.5S` |Timeout for ping probe used for checking healthiness of cached connections. Defaults to `PT0.5S`, which means 500 milliseconds. - @return timeout + Timeout |`prior-knowledge` |boolean |`false` |Prior knowledge of HTTP/2 capabilities of the server. If server we are connecting to does not support HTTP/2 and prior knowledge is set to `false`, only features supported by HTTP/1 will be available and attempts to use HTTP/2 specific will throw an UnsupportedOperationException. @@ -82,7 +82,7 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.http2/io/helidon/webclient/ht if prior knowledge is set to `false`, we will negotiate protocol using both HTTP/2 and HTTP/1, using the protocol supported by server. - @return whether to use prior knowledge of HTTP/2 + Whether to use prior knowledge of HTTP/2 |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webclient_websocket_WsClient.adoc b/docs/src/main/asciidoc/config/io_helidon_webclient_websocket_WsClient.adoc index d89f7bf4658..cde9c2dc9ae 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webclient_websocket_WsClient.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webclient_websocket_WsClient.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,7 +44,7 @@ Type: link:{javadoc-base-url}/io.helidon.webclient.websocket/io/helidon/webclien |`protocol-config` |xref:{rootdir}/config/io_helidon_webclient_websocket_WsClientProtocolConfig.adoc[WsClientProtocolConfig] |`create()` |WebSocket specific configuration. - @return protocol specific configuration + Protocol specific configuration |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_ConnectionConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_ConnectionConfig.adoc index 237d158759b..4015f937e68 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_ConnectionConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_ConnectionConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -43,39 +43,39 @@ Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/Connecti |key |type |default value |description |`connect-timeout` |Duration |`PT10S` |Connect timeout. - Default is `DEFAULT_CONNECT_TIMEOUT_DURATION`. + Default is DEFAULT_CONNECT_TIMEOUT_DURATION. - @return connect timeout + Connect timeout |`keep-alive` |boolean |`true` |Configure socket keep alive. Default is `true`. - @return keep alive + Keep alive @see java.net.StandardSocketOptions#SO_KEEPALIVE |`read-timeout` |Duration |`PT30S` |Read timeout. - Default is `DEFAULT_READ_TIMEOUT_DURATION` + Default is DEFAULT_READ_TIMEOUT_DURATION - @return read timeout + Read timeout |`receive-buffer-size` |int |`32768` |Socket receive buffer size. - Default is `DEFAULT_SO_BUFFER_SIZE`. + Default is DEFAULT_SO_BUFFER_SIZE. - @return buffer size, in bytes + Buffer size, in bytes @see java.net.StandardSocketOptions#SO_RCVBUF |`reuse-address` |boolean |`true` |Socket reuse address. Default is `true`. - @return whether to reuse address + Whether to reuse address @see java.net.StandardSocketOptions#SO_REUSEADDR |`send-buffer-size` |int |`32768` |Socket send buffer size. - Default is `DEFAULT_SO_BUFFER_SIZE`. + Default is DEFAULT_SO_BUFFER_SIZE. - @return buffer size, in bytes + Buffer size, in bytes @see java.net.StandardSocketOptions#SO_SNDBUF -|`tcp-no-delay` |boolean |`false` |Disable Nagle's algorithm by setting +|`tcp-no-delay` |boolean |`false` |Disable Nagle's algorithm by setting TCP_NODELAY to true. This can result in better performance on Mac or newer linux kernels for some payload types. Default is `false`. - @return whether to use TCP_NODELAY, defaults to `false` + Whether to use TCP_NODELAY, defaults to `false` @see java.net.StandardSocketOptions#TCP_NODELAY |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc index 627d66874b2..5ab27735bf5 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,38 +44,38 @@ Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/Listener |`backlog` |int |`1024` |Accept backlog. - @return backlog + Backlog |`connection-config` |xref:{rootdir}/config/io_helidon_webserver_ConnectionConfig.adoc[ConnectionConfig] |{nbsp} |Configuration of a connection (established from client against our server). - @return connection configuration + Connection configuration |`connection-options` |xref:{rootdir}/config/io_helidon_common_socket_SocketOptions.adoc[SocketOptions] |{nbsp} |Options for connections accepted by this listener. This is not used to setup server connection. - @return socket options + Socket options |`content-encoding` |xref:{rootdir}/config/io_helidon_http_encoding_ContentEncodingContext.adoc[ContentEncodingContext] |{nbsp} |Configure the listener specific io.helidon.http.encoding.ContentEncodingContext. This method discards all previously registered ContentEncodingContext. If no content encoding context is registered, content encoding context of the webserver would be used. - @return content encoding context + Content encoding context |`host` |string |`0.0.0.0` |Host of the default socket. Defaults to all host addresses (`0.0.0.0`). - @return host address to listen on (for the default socket) -|`idle-connection-period` |Duration |`PT2M` |How often should we check for #idleConnectionTimeout(). + Host address to listen on (for the default socket) +|`idle-connection-period` |Duration |`PT2M` |How often should we check for idleConnectionTimeout(). Defaults to `PT2M` (2 minutes). - @return period of checking for idle connections + Period of checking for idle connections |`idle-connection-timeout` |Duration |`PT5M` |How long should we wait before closing a connection that has no traffic on it. Defaults to `PT5M` (5 minutes). Note that the timestamp is refreshed max. once per second, so this setting would be useless if configured for shorter periods of time (also not a very good support for connection keep alive, if the connections are killed so soon anyway). - @return timeout of idle connections + Timeout of idle connections |`max-concurrent-requests` |int |`-1` |Limits the number of requests that can be executed at the same time (the number of active virtual threads of requests). Defaults to `-1`, meaning "unlimited" - what the system allows. Also make sure that this number is higher than the expected time it takes to handle a single request in your application, as otherwise you may stop in-progress requests. - @return number of requests that can be processed on this listener, regardless of protocol + Number of requests that can be processed on this listener, regardless of protocol |`max-in-memory-entity` |int |`131072` |If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance when writing it. If bigger, streaming will be used. @@ -85,31 +85,31 @@ Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/Listener Default is 128Kb. - @return maximal number of bytes to buffer in memory for supported writers + Maximal number of bytes to buffer in memory for supported writers |`max-payload-size` |long |`-1` |Maximal number of bytes an entity may have. - If io.helidon.http.HeaderNames#CONTENT_LENGTH is used, this is checked immediately, - if io.helidon.http.HeaderValues#TRANSFER_ENCODING_CHUNKED is used, we will fail when the + If io.helidon.http.HeaderNames.CONTENT_LENGTH is used, this is checked immediately, + if io.helidon.http.HeaderValues.TRANSFER_ENCODING_CHUNKED is used, we will fail when the number of bytes read would exceed the max payload size. Defaults to unlimited (`-1`). - @return maximal number of bytes of entity + Maximal number of bytes of entity |`max-tcp-connections` |int |`-1` |Limits the number of connections that can be opened at a single point in time. Defaults to `-1`, meaning "unlimited" - what the system allows. - @return number of TCP connections that can be opened to this listener, regardless of protocol + Number of TCP connections that can be opened to this listener, regardless of protocol |`media-context` |xref:{rootdir}/config/io_helidon_http_media_MediaContext.adoc[MediaContext] |{nbsp} |Configure the listener specific io.helidon.http.media.MediaContext. This method discards all previously registered MediaContext. If no media context is registered, media context of the webserver would be used. - @return media context + Media context |`name` |string |`@default` |Name of this socket. Defaults to `@default`. Must be defined if more than one socket is needed. - @return name of the socket + Name of the socket |`port` |int |`0` |Port of the default socket. If configured to `0` (the default), server starts on a random port. - @return port to listen on (for the default socket) + Port to listen on (for the default socket) |`protocols` |io.helidon.webserver.spi.ProtocolConfig[] (service provider interface) |{nbsp} |Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1. As the order is not important (providers are ordered by weight by default), we can use a configuration as an object, such as: @@ -124,25 +124,28 @@ Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/Listener .... - @return all defined protocol configurations, loaded from service loader by default + All defined protocol configurations, loaded from service loader by default |`receive-buffer-size` |int |{nbsp} |Listener receive buffer size. - @return buffer size in bytes + Buffer size in bytes +|`requested-uri-discovery` |xref:{rootdir}/config/io_helidon_http_RequestedUriDiscoveryContext.adoc[RequestedUriDiscoveryContext] |{nbsp} |Requested URI discovery context. + + Discovery context |`shutdown-grace-period` |Duration |`PT0.5S` |Grace period in ISO 8601 duration format to allow running tasks to complete before listener's shutdown. Default is `500` milliseconds. Configuration file values example: `PT0.5S`, `PT2S`. - @return grace period + Grace period |`tls` |xref:{rootdir}/config/io_helidon_common_tls_Tls.adoc[Tls] |{nbsp} |Listener TLS configuration. - @return tls of this configuration + Tls of this configuration |`write-buffer-size` |int |`512` |Initial buffer size in bytes of java.io.BufferedOutputStream created internally to write data to a socket connection. Default is `512`. - @return initial buffer size used for writing + Initial buffer size used for writing |`write-queue-length` |int |`0` |Number of buffers queued for write operations. - @return maximal number of queued writes, defaults to 0 + Maximal number of queued writes, defaults to 0 |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc index 6b119686ec3..cd8f542191c 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,51 +46,52 @@ This is a standalone configuration type, prefix from configuration root: `server |`backlog` |int |`1024` |Accept backlog. - @return backlog + Backlog |`connection-config` |xref:{rootdir}/config/io_helidon_webserver_ConnectionConfig.adoc[ConnectionConfig] |{nbsp} |Configuration of a connection (established from client against our server). - @return connection configuration + Connection configuration |`connection-options` |xref:{rootdir}/config/io_helidon_common_socket_SocketOptions.adoc[SocketOptions] |{nbsp} |Options for connections accepted by this listener. This is not used to setup server connection. - @return socket options + Socket options |`content-encoding` |xref:{rootdir}/config/io_helidon_http_encoding_ContentEncodingContext.adoc[ContentEncodingContext] |{nbsp} |Configure the listener specific io.helidon.http.encoding.ContentEncodingContext. This method discards all previously registered ContentEncodingContext. If no content encoding context is registered, content encoding context of the webserver would be used. - @return content encoding context + Content encoding context |`features` |io.helidon.webserver.spi.ServerFeature[] (service provider interface) Such as: - xref:{rootdir}/config/io_helidon_webserver_observe_ObserveFeature.adoc[observe (ObserveFeature)] - xref:{rootdir}/config/io_helidon_webserver_context_ContextFeature.adoc[context (ContextFeature)] + - xref:{rootdir}/config/io_helidon_openapi_OpenApiFeature.adoc[openapi (OpenApiFeature)] - xref:{rootdir}/config/io_helidon_webserver_cors_CorsFeature.adoc[cors (CorsFeature)] - xref:{rootdir}/config/io_helidon_webserver_security_SecurityFeature.adoc[security (SecurityFeature)] - xref:{rootdir}/config/io_helidon_webserver_accesslog_AccessLogFeature.adoc[access-log (AccessLogFeature)] |{nbsp} |Server features allow customization of the server, listeners, or routings. - @return server features + Server features |`host` |string |`0.0.0.0` |Host of the default socket. Defaults to all host addresses (`0.0.0.0`). - @return host address to listen on (for the default socket) -|`idle-connection-period` |Duration |`PT2M` |How often should we check for #idleConnectionTimeout(). + Host address to listen on (for the default socket) +|`idle-connection-period` |Duration |`PT2M` |How often should we check for idleConnectionTimeout(). Defaults to `PT2M` (2 minutes). - @return period of checking for idle connections + Period of checking for idle connections |`idle-connection-timeout` |Duration |`PT5M` |How long should we wait before closing a connection that has no traffic on it. Defaults to `PT5M` (5 minutes). Note that the timestamp is refreshed max. once per second, so this setting would be useless if configured for shorter periods of time (also not a very good support for connection keep alive, if the connections are killed so soon anyway). - @return timeout of idle connections + Timeout of idle connections |`max-concurrent-requests` |int |`-1` |Limits the number of requests that can be executed at the same time (the number of active virtual threads of requests). Defaults to `-1`, meaning "unlimited" - what the system allows. Also make sure that this number is higher than the expected time it takes to handle a single request in your application, as otherwise you may stop in-progress requests. - @return number of requests that can be processed on this listener, regardless of protocol + Number of requests that can be processed on this listener, regardless of protocol |`max-in-memory-entity` |int |`131072` |If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance when writing it. If bigger, streaming will be used. @@ -100,31 +101,31 @@ Such as: Default is 128Kb. - @return maximal number of bytes to buffer in memory for supported writers + Maximal number of bytes to buffer in memory for supported writers |`max-payload-size` |long |`-1` |Maximal number of bytes an entity may have. - If io.helidon.http.HeaderNames#CONTENT_LENGTH is used, this is checked immediately, - if io.helidon.http.HeaderValues#TRANSFER_ENCODING_CHUNKED is used, we will fail when the + If io.helidon.http.HeaderNames.CONTENT_LENGTH is used, this is checked immediately, + if io.helidon.http.HeaderValues.TRANSFER_ENCODING_CHUNKED is used, we will fail when the number of bytes read would exceed the max payload size. Defaults to unlimited (`-1`). - @return maximal number of bytes of entity + Maximal number of bytes of entity |`max-tcp-connections` |int |`-1` |Limits the number of connections that can be opened at a single point in time. Defaults to `-1`, meaning "unlimited" - what the system allows. - @return number of TCP connections that can be opened to this listener, regardless of protocol + Number of TCP connections that can be opened to this listener, regardless of protocol |`media-context` |xref:{rootdir}/config/io_helidon_http_media_MediaContext.adoc[MediaContext] |{nbsp} |Configure the listener specific io.helidon.http.media.MediaContext. This method discards all previously registered MediaContext. If no media context is registered, media context of the webserver would be used. - @return media context + Media context |`name` |string |`@default` |Name of this socket. Defaults to `@default`. Must be defined if more than one socket is needed. - @return name of the socket + Name of the socket |`port` |int |`0` |Port of the default socket. If configured to `0` (the default), server starts on a random port. - @return port to listen on (for the default socket) + Port to listen on (for the default socket) |`protocols` |io.helidon.webserver.spi.ProtocolConfig[] (service provider interface) |{nbsp} |Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1. As the order is not important (providers are ordered by weight by default), we can use a configuration as an object, such as: @@ -139,35 +140,38 @@ Such as: .... - @return all defined protocol configurations, loaded from service loader by default + All defined protocol configurations, loaded from service loader by default |`receive-buffer-size` |int |{nbsp} |Listener receive buffer size. - @return buffer size in bytes + Buffer size in bytes +|`requested-uri-discovery` |xref:{rootdir}/config/io_helidon_http_RequestedUriDiscoveryContext.adoc[RequestedUriDiscoveryContext] |{nbsp} |Requested URI discovery context. + + Discovery context |`shutdown-grace-period` |Duration |`PT0.5S` |Grace period in ISO 8601 duration format to allow running tasks to complete before listener's shutdown. Default is `500` milliseconds. Configuration file values example: `PT0.5S`, `PT2S`. - @return grace period + Grace period |`shutdown-hook` |boolean |`true` |When true the webserver registers a shutdown hook with the JVM Runtime. Defaults to true. Set this to false such that a shutdown hook is not registered. - @return whether to register a shutdown hook + Whether to register a shutdown hook |`sockets` |xref:{rootdir}/config/io_helidon_webserver_ListenerConfig.adoc[Map<string, ListenerConfig>] |{nbsp} |Socket configurations. - Note that socket named `WebServer#DEFAULT_SOCKET_NAME` cannot be used, + Note that socket named WebServer.DEFAULT_SOCKET_NAME cannot be used, configure the values on the server directly. - @return map of listener configurations, except for the default one + Map of listener configurations, except for the default one |`tls` |xref:{rootdir}/config/io_helidon_common_tls_Tls.adoc[Tls] |{nbsp} |Listener TLS configuration. - @return tls of this configuration + Tls of this configuration |`write-buffer-size` |int |`512` |Initial buffer size in bytes of java.io.BufferedOutputStream created internally to write data to a socket connection. Default is `512`. - @return initial buffer size used for writing + Initial buffer size used for writing |`write-queue-length` |int |`0` |Number of buffers queued for write operations. - @return maximal number of queued writes, defaults to 0 + Maximal number of queued writes, defaults to 0 |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc index 04ef4155704..56eca6ce309 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -54,9 +54,11 @@ This type provides the following service implementations: |`enabled` |boolean |`true` |Whether this feature will be enabled. - @return whether enabled + Whether enabled |`format` |string |{nbsp} |The format for log entries (similar to the Apache `LogFormat`). - + +++++ +
    @@ -109,20 +111,22 @@ This type provides the following service implementations:
    Log format elements
    %hHeaderLogEntry
    +++++ - @return format string, such as `%h %l %u %t %r %b %{Referer`i} -|`logger-name` |string |`io.helidon.webserver.AccessLog` |Name of the logger used to obtain access log logger from System#getLogger(String). - Defaults to `AccessLogFeature#DEFAULT_LOGGER_NAME`. - @return name of the logger to use + Format string, such as `%h %l %u %t %r %b %{Referer`i} +|`logger-name` |string |`io.helidon.webserver.AccessLog` |Name of the logger used to obtain access log logger from System.getLogger(String). + Defaults to AccessLogFeature.DEFAULT_LOGGER_NAME. + + Name of the logger to use |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets. The logger used will have the expected logger with a suffix of the socket name. - @return socket names to register on, defaults to empty (all available sockets) + Socket names to register on, defaults to empty (all available sockets) |`weight` |double |`1000.0` |Weight of the access log feature. We need to log access for anything happening on the server, so weight is high: - `io.helidon.webserver.accesslog.AccessLogFeature#WEIGHT`. + io.helidon.webserver.accesslog.AccessLogFeature.WEIGHT. - @return weight of the feature + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogFeature.adoc index 04ef4155704..56eca6ce309 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogFeature.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_accesslog_AccessLogFeature.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -54,9 +54,11 @@ This type provides the following service implementations: |`enabled` |boolean |`true` |Whether this feature will be enabled. - @return whether enabled + Whether enabled |`format` |string |{nbsp} |The format for log entries (similar to the Apache `LogFormat`). - + +++++ +
    @@ -109,20 +111,22 @@ This type provides the following service implementations:
    Log format elements
    %hHeaderLogEntry
    +++++ - @return format string, such as `%h %l %u %t %r %b %{Referer`i} -|`logger-name` |string |`io.helidon.webserver.AccessLog` |Name of the logger used to obtain access log logger from System#getLogger(String). - Defaults to `AccessLogFeature#DEFAULT_LOGGER_NAME`. - @return name of the logger to use + Format string, such as `%h %l %u %t %r %b %{Referer`i} +|`logger-name` |string |`io.helidon.webserver.AccessLog` |Name of the logger used to obtain access log logger from System.getLogger(String). + Defaults to AccessLogFeature.DEFAULT_LOGGER_NAME. + + Name of the logger to use |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets. The logger used will have the expected logger with a suffix of the socket name. - @return socket names to register on, defaults to empty (all available sockets) + Socket names to register on, defaults to empty (all available sockets) |`weight` |double |`1000.0` |Weight of the access log feature. We need to log access for anything happening on the server, so weight is high: - `io.helidon.webserver.accesslog.AccessLogFeature#WEIGHT`. + io.helidon.webserver.accesslog.AccessLogFeature.WEIGHT. - @return weight of the feature + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_context_ContextFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_context_ContextFeature.adoc index d947cc8c3a6..47a1a21772a 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_context_ContextFeature.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_context_ContextFeature.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -54,11 +54,11 @@ This type provides the following service implementations: |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets. - @return socket names to register on, defaults to empty (all available sockets) + Socket names to register on, defaults to empty (all available sockets) |`weight` |double |`1100.0` |Weight of the context feature. As it is used by other features, the default is quite high: - `io.helidon.webserver.context.ContextFeature#WEIGHT`. + io.helidon.webserver.context.ContextFeature.WEIGHT. - @return weight of the feature + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc index 40456b7f25f..9531104cc8b 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,6 +44,17 @@ This type provides the following service implementations: == Configuration options +.Required configuration options +[cols="3,3a,2,5a"] +|=== +|key |type |default value |description + +|`enabled` |boolean |{nbsp} |This feature can be disabled. + + Whether the feature is enabled + +|=== + .Optional configuration options @@ -52,16 +63,13 @@ This type provides the following service implementations: |=== |key |type |default value |description -|`enabled` |boolean |`true` |This feature can be disabled. - - @return whether the feature is enabled |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets. - @return socket names to register on, defaults to empty (all available sockets) + Socket names to register on, defaults to empty (all available sockets) |`weight` |double |`950.0` |Weight of the CORS feature. As it is used by other features, the default is quite high: - `CorsFeature#WEIGHT`. + CorsFeature.WEIGHT. - @return weight of the feature + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc index 40456b7f25f..9531104cc8b 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,6 +44,17 @@ This type provides the following service implementations: == Configuration options +.Required configuration options +[cols="3,3a,2,5a"] +|=== +|key |type |default value |description + +|`enabled` |boolean |{nbsp} |This feature can be disabled. + + Whether the feature is enabled + +|=== + .Optional configuration options @@ -52,16 +63,13 @@ This type provides the following service implementations: |=== |key |type |default value |description -|`enabled` |boolean |`true` |This feature can be disabled. - - @return whether the feature is enabled |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets. - @return socket names to register on, defaults to empty (all available sockets) + Socket names to register on, defaults to empty (all available sockets) |`weight` |double |`950.0` |Weight of the CORS feature. As it is used by other features, the default is quite high: - `CorsFeature#WEIGHT`. + CorsFeature.WEIGHT. - @return weight of the feature + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc index a69950a7b63..7a8b58ea104 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,27 +49,27 @@ This type provides the following service implementations: |`continue-immediately` |boolean |`false` |When true WebServer answers to expect continue with 100 continue immediately, not waiting for user to actually request the data. - @return if `true` answer with 100 continue immediately after expect continue + If `true` answer with 100 continue immediately after expect continue |`max-headers-size` |int |`16384` |Maximal size of received headers in bytes. - @return maximal header size + Maximal header size |`max-prologue-length` |int |`2048` |Maximal size of received HTTP prologue (GET /path HTTP/1.1). - @return maximal size in bytes + Maximal size in bytes |`recv-log` |boolean |`true` |Logging of received packets. Uses trace and debug levels on logger of Http1LoggingConnectionListener with suffix of `.recv``. - @return `true` if logging should be enabled for received packets, `false` if no logging should be done + `true` if logging should be enabled for received packets, `false` if no logging should be done |`requested-uri-discovery` |xref:{rootdir}/config/io_helidon_http_RequestedUriDiscoveryContext.adoc[RequestedUriDiscoveryContext] |{nbsp} |Requested URI discovery settings. - @return settings for computing the requested URI + Settings for computing the requested URI |`send-log` |boolean |`true` |Logging of sent packets. Uses trace and debug levels on logger of Http1LoggingConnectionListener with suffix of `.send``. - @return `true` if logging should be enabled for sent packets, `false` if no logging should be done + `true` if logging should be enabled for sent packets, `false` if no logging should be done |`validate-path` |boolean |`true` |If set to false, any path is accepted (even containing illegal characters). - @return whether to validate path + Whether to validate path |`validate-request-headers` |boolean |`true` |Whether to validate headers. If set to false, any value is accepted, otherwise validates headers + known headers are validated by format @@ -79,7 +79,7 @@ This type provides the following service implementations: Defaults to `true`. - @return whether to validate headers + Whether to validate headers |`validate-response-headers` |boolean |`false` |Whether to validate headers. If set to false, any value is accepted, otherwise validates headers + known headers are validated by format @@ -89,7 +89,7 @@ This type provides the following service implementations: Defaults to `false` as user has control on the header creation. - @return whether to validate headers + Whether to validate headers |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc index 5d1818c2c41..c6324a3b995 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -52,14 +52,18 @@ This type provides the following service implementations: before the next blocking iteration. Default value is `PT0.1S`. - + +++++ +
    ISO_8601 format examples:
    PT0.1S100 milliseconds
    PT0.5S500 milliseconds
    PT2S2 seconds
    +++++ - @return duration + + Duration @see ISO_8601 Durations |`initial-window-size` |int |`1048576` |This setting indicates the sender's maximum window size in bytes for stream-level flow control. Default and maximum value is 231-1 = 2147483647 bytes. This setting affects the window size @@ -67,36 +71,51 @@ This type provides the following service implementations: Any value greater than 2147483647 causes an error. Any value smaller than initial window size causes an error. See RFC 9113 section 6.9.1 for details. - @return maximum window size in bytes + Maximum window size in bytes |`max-concurrent-streams` |long |`8192` |Maximum number of concurrent streams that the server will allow. Defaults to `8192`. This limit is directional: it applies to the number of streams that the sender permits the receiver to create. It is recommended that this value be no smaller than 100 to not unnecessarily limit parallelism See RFC 9113 section 6.5.2 for details. - @return maximal number of concurrent streams + Maximal number of concurrent streams +|`max-empty-frames` |int |`10` |Maximum number of consecutive empty frames allowed on connection. + + Max number of consecutive empty frames |`max-frame-size` |int |`16384` |The size of the largest frame payload that the sender is willing to receive in bytes. Default value is `16384` and maximum value is 224-1 = 16777215 bytes. See RFC 9113 section 6.5.2 for details. - @return maximal frame size + Maximal frame size |`max-header-list-size` |long |`8192` |The maximum field section size that the sender is prepared to accept in bytes. See RFC 9113 section 6.5.2 for details. Default is 8192. - @return maximal header list size in bytes + Maximal header list size in bytes +|`max-rapid-resets` |int |`100` |Maximum number of rapid resets(stream RST sent by client before any data have been sent by server). + When reached within rapidResetCheckPeriod(), GOAWAY is sent to client and connection is closed. + Default value is `100`. + + Maximum number of rapid resets + @see CVE-2023-44487 +|`rapid-reset-check-period` |Duration |`PT10S` |Period for counting rapid resets(stream RST sent by client before any data have been sent by server). + Default value is `PT10S`. + + Duration + @see CVE-2023-44487 + @see ISO_8601 Durations |`requested-uri-discovery` |xref:{rootdir}/config/io_helidon_http_RequestedUriDiscoveryContext.adoc[RequestedUriDiscoveryContext] |{nbsp} |Requested URI discovery settings. - @return settings for computing the requested URI + Settings for computing the requested URI |`send-error-details` |boolean |`false` |Whether to send error message over HTTP to client. Defaults to `false`, as exception message may contain internal information that could be used as an attack vector. Use with care and in cases where both server and clients are under your full control (such as for testing). - @return whether to send error messages over the network + Whether to send error messages over the network |`validate-path` |boolean |`true` |If set to false, any path is accepted (even containing illegal characters). - @return whether to validate path + Whether to validate path |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc index 6fa9dbf8566..16f38a02e30 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -54,10 +54,10 @@ This type provides the following service implementations: |`cors` |xref:{rootdir}/config/io_helidon_cors_CrossOriginConfig.adoc[CrossOriginConfig] |`@io.helidon.cors.CrossOriginConfig@.create()` |Cors support inherited by each observe provider, unless explicitly configured. - @return cors support to use + Cors support to use |`enabled` |boolean |`true` |Whether the observe support is enabled. - @return `false` to disable observe feature + `false` to disable observe feature |`endpoint` |string |`/observe` |Root endpoint to use for observe providers. By default, all observe endpoint are under this root endpoint. Example: @@ -65,31 +65,31 @@ This type provides the following service implementations: If root endpoint is `/observe` (the default), and default health endpoint is `health` (relative), health endpoint would be `/observe/health`. - @return endpoint to use + Endpoint to use |`observers` |io.helidon.webserver.observe.spi.Observer[] (service provider interface) Such as: - - xref:{rootdir}/config/io_helidon_webserver_observe_config_ConfigObserver.adoc[ConfigObserver] - - xref:{rootdir}/config/io_helidon_webserver_observe_info_InfoObserver.adoc[InfoObserver] - xref:{rootdir}/config/io_helidon_webserver_observe_log_LogObserver.adoc[LogObserver] - xref:{rootdir}/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc[TracingObserver] + - xref:{rootdir}/config/io_helidon_webserver_observe_config_ConfigObserver.adoc[ConfigObserver] + - xref:{rootdir}/config/io_helidon_webserver_observe_info_InfoObserver.adoc[InfoObserver] - xref:{rootdir}/config/io_helidon_webserver_observe_metrics_MetricsObserver.adoc[metrics (MetricsObserver)] - xref:{rootdir}/config/io_helidon_webserver_observe_health_HealthObserver.adoc[health (HealthObserver)] |{nbsp} |Observers to use with this observe features. Each observer type is registered only once, unless it uses a custom name (default name is the same as the type). - @return list of observers to use in this feature + List of observers to use in this feature |`sockets` |string[] |{nbsp} |Sockets the observability endpoint should be exposed on. If not defined, defaults to the default socket - (`io.helidon.webserver.WebServer#DEFAULT_SOCKET_NAME`. + (io.helidon.webserver.WebServer.DEFAULT_SOCKET_NAME. Each observer may have its own configuration of sockets that are relevant to it, this only controls the endpoints! - @return list of sockets to register observe endpoint on + List of sockets to register observe endpoint on |`weight` |double |`80.0` |Change the weight of this feature. This may change the order of registration of this feature. - By default, observability weight is `ObserveFeature#WEIGHT` so it is registered after routing. + By default, observability weight is ObserveFeature.WEIGHT so it is registered after routing. - @return weight to use + Weight to use |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserverConfigBase.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserverConfigBase.adoc index 611b61ed9f4..2419abb5a7c 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserverConfigBase.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserverConfigBase.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,7 +44,7 @@ Type: link:{javadoc-base-url}/io.helidon.webserver.observe/io/helidon/webserver/ |`enabled` |boolean |`true` |Whether this observer is enabled. - @return `false` to disable observer + `false` to disable observer |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc index 8e65b0ab82f..43eb920fc73 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,7 +49,7 @@ This type provides the following service implementations: |`endpoint` |string |`config` | |`permit-all` |boolean |{nbsp} |Permit all access, even when not authorized. - @return whether to permit access for anybody + Whether to permit access for anybody |`secrets` |string[] |`.*password, .*passphrase, .*secret` |Secret patterns (regular expressions) to exclude from output. Any pattern that matches a key will cause the output to be obfuscated and not contain the value. @@ -59,7 +59,7 @@ This type provides the following service implementations: - `.*passphrase` - `.*secret` -@return set of regular expression patterns for keys, where values should be excluded from output +Set of regular expression patterns for keys, where values should be excluded from output |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_health_HealthObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_health_HealthObserver.adoc index b1e17758329..6af7c2322a1 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_health_HealthObserver.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_health_HealthObserver.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,19 +49,19 @@ This type provides the following service implementations: |key |type |default value |description |`details` |boolean |`false` |Whether details should be printed. - By default, health only returns a io.helidon.http.Status#NO_CONTENT_204 for success, - io.helidon.http.Status#SERVICE_UNAVAILABLE_503 for health down, - and io.helidon.http.Status#INTERNAL_SERVER_ERROR_500 in case of error with no entity. - When details are enabled, health returns io.helidon.http.Status#OK_200 for success, same codes + By default, health only returns a io.helidon.http.Status.NO_CONTENT_204 for success, + io.helidon.http.Status.SERVICE_UNAVAILABLE_503 for health down, + and io.helidon.http.Status.INTERNAL_SERVER_ERROR_500 in case of error with no entity. + When details are enabled, health returns io.helidon.http.Status.OK_200 for success, same codes otherwise and a JSON entity with detailed information about each health check executed. - @return set to `true` to enable details + Set to `true` to enable details |`endpoint` |string |`health` | |`use-system-services` |boolean |`true` |Whether to use services discovered by java.util.ServiceLoader. By default, all io.helidon.health.spi.HealthCheckProvider based health checks are added. - @return set to `false` to disable discovery + Set to `false` to disable discovery |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc index 7f0db5d061b..4016a71d1af 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,7 +49,7 @@ This type provides the following service implementations: |`endpoint` |string |`info` | |`values` |Map<string, string> |{nbsp} |Values to be exposed using this observability endpoint. - @return value map + Value map |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc index 268b56c4cfc..69402e92a7e 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -49,10 +49,10 @@ This type provides the following service implementations: |`endpoint` |string |`log` | |`permit-all` |boolean |{nbsp} |Permit all access, even when not authorized. - @return whether to permit access for anybody + Whether to permit access for anybody |`stream` |xref:{rootdir}/config/io_helidon_webserver_observe_log_LogStreamConfig.adoc[LogStreamConfig] |`@io.helidon.webserver.observe.log.LogStreamConfig@.create()` |Configuration of log stream. - @return log stream configuration + Log stream configuration |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogStreamConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogStreamConfig.adoc index db41de1f11f..9d82d3619cc 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogStreamConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogStreamConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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,20 +45,20 @@ Type: link:{javadoc-base-url}/io.helidon.webserver.observe.log/io/helidon/webser |`content-type` |HttpMediaType |`@io.helidon.http.HttpMediaTypes@.PLAINTEXT_UTF_8` | |`enabled` |boolean |`true` |Whether stream is enabled. - @return whether to allow streaming of log statements + Whether to allow streaming of log statements |`idle-message-timeout` |Duration |`PT5S` |How long to wait before we send the idle message, to make sure we keep the stream alive. - @return if no messages appear within this duration, and idle message will be sent + If no messages appear within this duration, and idle message will be sent @see #idleString() |`idle-string` |string |`% -` |String sent when there are no log messages within the #idleMessageTimeout(). +` |String sent when there are no log messages within the idleMessageTimeout(). - @return string to write over the network when no log messages are received + String to write over the network when no log messages are received |`queue-size` |int |`100` |Length of the in-memory queue that buffers log messages from loggers before sending them over the network. If the messages are produced faster than we can send them to client, excess messages are DISCARDED, and will not be sent. - @return size of the in-memory queue for log messages + Size of the in-memory queue for log messages |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_metrics_MetricsObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_metrics_MetricsObserver.adoc index 43468d4aced..6153198951e 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_metrics_MetricsObserver.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_metrics_MetricsObserver.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -50,33 +50,33 @@ This type provides the following service implementations: |`app-name` |string |{nbsp} |Value for the application tag to be added to each meter ID. - @return application tag value + Application tag value |`app-tag-name` |string |{nbsp} |Name for the application tag to be added to each meter ID. - @return application tag name + Application tag name |`enabled` |boolean |`true` |Whether metrics functionality is enabled. - @return if metrics are configured to be enabled + If metrics are configured to be enabled |`endpoint` |string |`metrics` | |`key-performance-indicators` |xref:{rootdir}/config/io_helidon_metrics_api_KeyPerformanceIndicatorMetricsConfig.adoc[KeyPerformanceIndicatorMetricsConfig] |{nbsp} |Key performance indicator metrics settings. - @return key performance indicator metrics settings -|`permit-all` |boolean |{nbsp} |Whether to allow anybody to access the endpoint. + Key performance indicator metrics settings +|`permit-all` |boolean |`true` |Whether to allow anybody to access the endpoint. - @return whether to permit access to metrics endpoint to anybody, defaults to `true` + Whether to permit access to metrics endpoint to anybody, defaults to `true` @see #roles() |`rest-request-enabled` |boolean |{nbsp} |Whether automatic REST request metrics should be measured. - @return true/false -|`roles` |string[] |{nbsp} |Hints for role names the user is expected to be in. + True/false +|`roles` |string[] |`observe` |Hints for role names the user is expected to be in. - @return list of hints + List of hints |`scoping` |xref:{rootdir}/config/io_helidon_metrics_api_ScopingConfig.adoc[ScopingConfig] |{nbsp} |Settings related to scoping management. - @return scoping settings + Scoping settings |`tags` |xref:{rootdir}/config/io_helidon_metrics_api_Tag.adoc[Tag[]] |{nbsp} |Global tags. - @return name/value pairs for global tags + Name/value pairs for global tags |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc index 5c17c29cbe3..fc0e267850e 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -48,24 +48,38 @@ This type provides the following service implementations: |`env-config` |TracingConfig |`TracingConfig.ENABLED` |Use the provided configuration as a default for any request. - @return default web server tracing configuration -|`path-configs` |PathTracingConfig[] |`new @java.util.ArrayList@(@java.util.List@.of(PathTracingConfig.builder() + Default web server tracing configuration +|`paths` |PathTracingConfig[] |`new @java.util.ArrayList@(@java.util.List@.of(PathTracingConfig.builder() .path("/metrics/*") .tracingConfig(TracingConfig.DISABLED) - .build(), PathTracingConfig.builder() + .build(), + PathTracingConfig.builder() + .path("/observe/metrics/*") + .tracingConfig(TracingConfig.DISABLED) + .build(), + PathTracingConfig.builder() .path("/health/*") .tracingConfig(TracingConfig.DISABLED) - .build(), PathTracingConfig.builder() + .build(), + PathTracingConfig.builder() + .path("/observe/health/*") + .tracingConfig(TracingConfig.DISABLED) + .build(), + PathTracingConfig.builder() .path("/openapi/*") .tracingConfig(TracingConfig.DISABLED) + .build(), + PathTracingConfig.builder() + .path("/observe/openapi/*") + .tracingConfig(TracingConfig.DISABLED) .build()))` |Path specific configuration of tracing. - @return configuration of tracing for specific paths + Configuration of tracing for specific paths |`weight` |double |`900.0` |Weight of the feature registered with WebServer. Changing weight may cause tracing to be executed at a different time (such as after security, or even after all routes). Please understand feature weights before changing this order. - @return weight of tracing feature + Weight of tracing feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_security_PathsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_security_PathsConfig.adoc index 227915c92bf..c8edd4ef024 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_security_PathsConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_security_PathsConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,45 +44,45 @@ Type: link:{javadoc-base-url}/io.helidon.webserver.security/io/helidon/webserver |`audit` |boolean |{nbsp} |Whether to audit this request - defaults to false, if enabled, request is audited with event type "request". - @return whether to audit -|`audit-event-type` |string |{nbsp} |Override for event-type, defaults to `SecurityHandler#DEFAULT_AUDIT_EVENT_TYPE`. + Whether to audit +|`audit-event-type` |string |{nbsp} |Override for event-type, defaults to SecurityHandler.DEFAULT_AUDIT_EVENT_TYPE. - @return audit event type to use -|`audit-message-format` |string |{nbsp} |Override for audit message format, defaults to `SecurityHandler#DEFAULT_AUDIT_MESSAGE_FORMAT`. + Audit event type to use +|`audit-message-format` |string |{nbsp} |Override for audit message format, defaults to SecurityHandler.DEFAULT_AUDIT_MESSAGE_FORMAT. - @return audit message format to use + Audit message format to use |`authenticate` |boolean |{nbsp} |If called, request will go through authentication process - defaults to false (even if authorize is true). - @return whether to authenticate or not + Whether to authenticate or not |`authentication-optional` |boolean |{nbsp} |If called, authentication failure will not abort request and will continue as anonymous (defaults to false). - @return whether authn is optional + Whether authn is optional |`authenticator` |string |{nbsp} |Use a named authenticator (as supported by security - if not defined, default authenticator is used). Will enable authentication. - @return name of authenticator as configured in io.helidon.security.Security + Name of authenticator as configured in io.helidon.security.Security |`authorize` |boolean |{nbsp} |Enable authorization for this route. - @return whether to authorize + Whether to authorize |`authorizer` |string |{nbsp} |Use a named authorizer (as supported by security - if not defined, default authorizer is used, if none defined, all is permitted). Will enable authorization. - @return name of authorizer as configured in io.helidon.security.Security + Name of authorizer as configured in io.helidon.security.Security |`methods` |Method[] |{nbsp} | |`path` |string |{nbsp} | |`roles-allowed` |string[] |{nbsp} |An array of allowed roles for this path - must have a security provider supporting roles (either authentication or authorization provider). This method enables authentication and authorization (you can disable them again by calling - SecurityHandler#skipAuthorization() - and #authenticationOptional() if needed). + SecurityHandler.skipAuthorization() + and authenticationOptional() if needed). - @return if subject is any of these roles, allow access + If subject is any of these roles, allow access |`sockets` |string[] |`@default` | |`sockets` |string[] |{nbsp} |List of sockets this configuration should be applied to. If empty, the configuration is applied to all configured sockets. - @return list of sockets + List of sockets |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityFeature.adoc index b67bb87ee2a..4b294500092 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityFeature.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityFeature.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -54,22 +54,22 @@ This type provides the following service implementations: |`defaults` |xref:{rootdir}/config/io_helidon_webserver_security_SecurityHandler.adoc[SecurityHandler] |`SecurityHandler.create()` |The default security handler. - @return security handler defaults + Security handler defaults |`paths` |xref:{rootdir}/config/io_helidon_webserver_security_PathsConfig.adoc[PathsConfig[]] |{nbsp} |Configuration for webserver paths. - @return path configuration + Path configuration |`security` |xref:{rootdir}/config/io_helidon_security_Security.adoc[Security] |{nbsp} |Security associated with this feature. If not specified here, the feature uses security registered with - io.helidon.common.context.Contexts#globalContext(), if not found, it creates a new + io.helidon.common.context.Contexts.globalContext(), if not found, it creates a new instance from root of configuration (using `security` key). This configuration allows usage of a different security instance for a specific security feature setup. - @return security instance to be used to handle security in this feature configuration + Security instance to be used to handle security in this feature configuration |`weight` |double |`800.0` |Weight of the security feature. Value is: - `io.helidon.webserver.security.SecurityFeature#WEIGHT`. + io.helidon.webserver.security.SecurityFeature.WEIGHT. - @return weight of the feature + Weight of the feature |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityHandler.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityHandler.adoc index 9308d2c6b97..413683d1227 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityHandler.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_security_SecurityHandler.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -44,42 +44,42 @@ Type: link:{javadoc-base-url}/io.helidon.webserver.security/io/helidon/webserver |`audit` |boolean |{nbsp} |Whether to audit this request - defaults to false, if enabled, request is audited with event type "request". - @return whether to audit -|`audit-event-type` |string |{nbsp} |Override for event-type, defaults to `SecurityHandler#DEFAULT_AUDIT_EVENT_TYPE`. + Whether to audit +|`audit-event-type` |string |{nbsp} |Override for event-type, defaults to SecurityHandler.DEFAULT_AUDIT_EVENT_TYPE. - @return audit event type to use -|`audit-message-format` |string |{nbsp} |Override for audit message format, defaults to `SecurityHandler#DEFAULT_AUDIT_MESSAGE_FORMAT`. + Audit event type to use +|`audit-message-format` |string |{nbsp} |Override for audit message format, defaults to SecurityHandler.DEFAULT_AUDIT_MESSAGE_FORMAT. - @return audit message format to use + Audit message format to use |`authenticate` |boolean |{nbsp} |If called, request will go through authentication process - defaults to false (even if authorize is true). - @return whether to authenticate or not + Whether to authenticate or not |`authentication-optional` |boolean |{nbsp} |If called, authentication failure will not abort request and will continue as anonymous (defaults to false). - @return whether authn is optional + Whether authn is optional |`authenticator` |string |{nbsp} |Use a named authenticator (as supported by security - if not defined, default authenticator is used). Will enable authentication. - @return name of authenticator as configured in io.helidon.security.Security + Name of authenticator as configured in io.helidon.security.Security |`authorize` |boolean |{nbsp} |Enable authorization for this route. - @return whether to authorize + Whether to authorize |`authorizer` |string |{nbsp} |Use a named authorizer (as supported by security - if not defined, default authorizer is used, if none defined, all is permitted). Will enable authorization. - @return name of authorizer as configured in io.helidon.security.Security + Name of authorizer as configured in io.helidon.security.Security |`roles-allowed` |string[] |{nbsp} |An array of allowed roles for this path - must have a security provider supporting roles (either authentication or authorization provider). This method enables authentication and authorization (you can disable them again by calling - SecurityHandler#skipAuthorization() - and #authenticationOptional() if needed). + SecurityHandler.skipAuthorization() + and authenticationOptional() if needed). - @return if subject is any of these roles, allow access + If subject is any of these roles, allow access |`sockets` |string[] |{nbsp} |List of sockets this configuration should be applied to. If empty, the configuration is applied to all configured sockets. - @return list of sockets + List of sockets |=== diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_servicecommon_RestServiceSettings_Builder.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_servicecommon_RestServiceSettings_Builder.adoc new file mode 100644 index 00000000000..32e941a325d --- /dev/null +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_servicecommon_RestServiceSettings_Builder.adoc @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////// + + 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] +:description: Configuration of io.helidon.webserver.servicecommon.RestServiceSettings.Builder +:keywords: helidon, config, io.helidon.webserver.servicecommon.RestServiceSettings.Builder +:basic-table-intro: The table below lists the configuration keys that configure io.helidon.webserver.servicecommon.RestServiceSettings.Builder +include::{rootdir}/includes/attributes.adoc[] + += Builder (webserver.servicecommon.RestServiceSettings) Configuration + +// tag::config[] + + +Type: link:{javadoc-base-url}/io.helidon.webserver.servicecommon.RestServiceSettings/io/helidon/webserver/servicecommon/RestServiceSettings/Builder.html[io.helidon.webserver.servicecommon.RestServiceSettings.Builder] + + + + +== Configuration options + + + +.Optional configuration options +[cols="3,3a,2,5a"] + +|=== +|key |type |default value |description + +|`cors` |xref:{rootdir}/config/io_helidon_cors_CrossOriginConfig.adoc[Map<string, CrossOriginConfig>] |{nbsp} |Sets the cross-origin config builder for use in establishing CORS support for the service endpoints. +|`enabled` |boolean |`true` |Is this service enabled or not. +|`routing` |string |{nbsp} |Sets the routing name to use for setting up the service's endpoint. +|`web-context` |string |{nbsp} |Sets the web context to use for the service's endpoint. + +|=== + +// end::config[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc index e7d2c28ccf4..884ee43c457 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -46,12 +46,16 @@ This type provides the following service implementations: |=== |key |type |default value |description +|`max-frame-length` |int |`1048576` |Max WebSocket frame size supported by the server on a read operation. + Default is 1 MB. + + Max frame size to read |`name` |string |`websocket` |Name of this configuration. - @return configuration name + Configuration name |`origins` |string[] |{nbsp} |WebSocket origins. - @return origins + Origins |=== diff --git a/docs/src/main/asciidoc/config/io_opentracing_Tracer.adoc b/docs/src/main/asciidoc/config/io_opentracing_Tracer.adoc index 93beb75f9eb..f8e347a05fa 100644 --- a/docs/src/main/asciidoc/config/io_opentracing_Tracer.adoc +++ b/docs/src/main/asciidoc/config/io_opentracing_Tracer.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - 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. @@ -47,7 +47,7 @@ This is a standalone configuration type, prefix from configuration root: `tracin |key |type |default value |description |`api-version` |Version (V1, V2) |`V2` |Version of Zipkin API to use. - Defaults to Version#V2. + Defaults to Version.V2. |=== From 1f0195e17f9f5deb004da06990bc180115232586 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 7 Jun 2024 14:34:16 +0200 Subject: [PATCH 047/245] Updates to native image configuration to support latest release of GraalVM. (#8838) --- .../native-image.properties | 18 ++++++++++++++++++ .../native-image.properties | 7 ++++++- .../native-image.properties | 17 +++++++++++++++++ .../oidc/TenantAuthenticationHandler.java | 5 +++-- 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 common/configurable/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-configurable/native-image.properties create mode 100644 logging/jul/src/main/resources/META-INF/native-image/io.helidon.logging/helidon-logging-jul/native-image.properties diff --git a/common/configurable/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-configurable/native-image.properties b/common/configurable/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-configurable/native-image.properties new file mode 100644 index 00000000000..7c562f87e98 --- /dev/null +++ b/common/configurable/src/main/resources/META-INF/native-image/io.helidon.common/helidon-common-configurable/native-image.properties @@ -0,0 +1,18 @@ +# +# 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. +# + +Args=--initialize-at-build-time=io.helidon.common.configurable.LruCache \ + --initialize-at-build-time=io.helidon.common.configurable.LruCacheConfig diff --git a/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties b/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties index e899a726713..8f98ca6b54e 100644 --- a/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/native-image.properties +++ b/integrations/graal/mp-native-image-extension/src/main/resources/META-INF/native-image/io.helidon.integrations.graal/helidon-mp-graal-native-image-extension/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. @@ -40,7 +40,12 @@ Args=--features=io.helidon.integrations.graal.mp.nativeimage.extension.HelidonMp --initialize-at-build-time=jakarta.interceptor \ --initialize-at-build-time=jakarta.annotation \ --initialize-at-build-time=com.sun.beans.TypeResolver \ + --initialize-at-build-time=com.sun.beans.WeakCache \ --initialize-at-build-time=java.beans.PropertyDescriptor \ --initialize-at-build-time=java.beans.MethodRef \ --initialize-at-build-time=org.yaml.snakeyaml \ + --initialize-at-build-time=org.jvnet.hk2 \ + --initialize-at-build-time=java.lang.annotation.Annotation \ + --initialize-at-build-time=io.helidon \ + --initialize-at-build-time=org.eclipse.microprofile \ --report-unsupported-elements-at-runtime diff --git a/logging/jul/src/main/resources/META-INF/native-image/io.helidon.logging/helidon-logging-jul/native-image.properties b/logging/jul/src/main/resources/META-INF/native-image/io.helidon.logging/helidon-logging-jul/native-image.properties new file mode 100644 index 00000000000..5e26c6713f2 --- /dev/null +++ b/logging/jul/src/main/resources/META-INF/native-image/io.helidon.logging/helidon-logging-jul/native-image.properties @@ -0,0 +1,17 @@ +# +# 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. +# + +Args=--initialize-at-build-time=io.helidon.logging.jul.JulMdcPropagator diff --git a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java index 69540afdca1..70dc6c23285 100644 --- a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java +++ b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java @@ -38,6 +38,7 @@ import java.util.stream.Collectors; import io.helidon.common.Errors; +import io.helidon.common.LazyValue; import io.helidon.common.parameters.Parameters; import io.helidon.http.HeaderNames; import io.helidon.http.HeaderValues; @@ -83,7 +84,7 @@ class TenantAuthenticationHandler { private static final System.Logger LOGGER = System.getLogger(TenantAuthenticationHandler.class.getName()); private static final TokenHandler PARAM_HEADER_HANDLER = TokenHandler.forHeader(OidcConfig.PARAM_HEADER_NAME); private static final TokenHandler PARAM_ID_HEADER_HANDLER = TokenHandler.forHeader(OidcConfig.PARAM_ID_HEADER_NAME); - private static final SecureRandom RANDOM = new SecureRandom(); + private static final LazyValue RANDOM = LazyValue.create(SecureRandom::new); private final boolean optional; private final OidcConfig oidcConfig; @@ -824,7 +825,7 @@ private static String generateRandomString() { int rightLimit = 122; // letter 'z' int targetStringLength = 10; - return RANDOM.ints(leftLimit, rightLimit + 1) + return RANDOM.get().ints(leftLimit, rightLimit + 1) .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) .limit(targetStringLength) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) From 026610cf6b93dabfc0c0e5f33688142f9baf7090 Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Mon, 10 Jun 2024 19:12:00 +0300 Subject: [PATCH 048/245] 4.x: Add uses io.helidon.dbclient.jdbc.spi.JdbcConnectionPoolProvider to io.helidon.dbclient.jdbc module (#8237) (#8850) --- dbclient/jdbc/src/main/java/module-info.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dbclient/jdbc/src/main/java/module-info.java b/dbclient/jdbc/src/main/java/module-info.java index d0b0762232e..5a70ccddcd0 100644 --- a/dbclient/jdbc/src/main/java/module-info.java +++ b/dbclient/jdbc/src/main/java/module-info.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. @@ -41,6 +41,7 @@ exports io.helidon.dbclient.jdbc.spi; uses io.helidon.dbclient.jdbc.JdbcClientProvider; + uses io.helidon.dbclient.jdbc.spi.JdbcConnectionPoolProvider; provides io.helidon.dbclient.spi.DbClientProvider with io.helidon.dbclient.jdbc.JdbcClientProvider; From 652f099f2cbf78ccd3c74f78ff083cc638f3518d Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Tue, 11 Jun 2024 07:37:15 +0200 Subject: [PATCH 049/245] 4.x: add helidon-logging-jul as a test dependency to some modules #779 (#8810) Signed-off-by: Jorge Bescos Gascon --- examples/microprofile/cors/pom.xml | 5 +++++ examples/microprofile/static-content/pom.xml | 5 +++++ http/tests/encoding/deflate/pom.xml | 5 +++++ http/tests/encoding/gzip/pom.xml | 5 +++++ integrations/micrometer/cdi/pom.xml | 5 +++++ microprofile/health/pom.xml | 5 +++++ microprofile/lra/jax-rs/pom.xml | 5 +++++ microprofile/metrics/pom.xml | 5 +++++ microprofile/security/pom.xml | 5 +++++ microprofile/tests/tck/tck-config/pom.xml | 5 +++++ microprofile/tests/tck/tck-telemetry/pom.xml | 5 +++++ microprofile/tracing/pom.xml | 5 +++++ security/providers/oidc-common/pom.xml | 5 +++++ security/providers/oidc/pom.xml | 5 +++++ tests/functional/context-propagation/pom.xml | 5 +++++ tests/functional/jax-rs-multiple-apps/pom.xml | 5 +++++ tests/functional/jax-rs-subresource/pom.xml | 5 +++++ tests/functional/mp-compression/pom.xml | 5 +++++ tests/functional/request-scope-cdi/pom.xml | 5 +++++ tests/functional/request-scope-injection/pom.xml | 5 +++++ tests/functional/request-scope/pom.xml | 5 +++++ tests/integration/jms/pom.xml | 5 +++++ tests/integration/jpa/appl/pom.xml | 5 +++++ tests/integration/jpa/simple/pom.xml | 5 +++++ tests/integration/kafka/pom.xml | 5 +++++ tracing/tests/it-tracing-client-zipkin/pom.xml | 5 +++++ webclient/tests/webclient/pom.xml | 5 +++++ webserver/testing/junit5/websocket/pom.xml | 5 +++++ webserver/tests/grpc/pom.xml | 5 +++++ webserver/tests/resource-limits/pom.xml | 5 +++++ 30 files changed, 150 insertions(+) diff --git a/examples/microprofile/cors/pom.xml b/examples/microprofile/cors/pom.xml index 468382208c1..86a58ad8791 100644 --- a/examples/microprofile/cors/pom.xml +++ b/examples/microprofile/cors/pom.xml @@ -75,6 +75,11 @@ helidon-http-media-jsonb test
    + + io.helidon.logging + helidon-logging-jul + test + diff --git a/examples/microprofile/static-content/pom.xml b/examples/microprofile/static-content/pom.xml index a5cf009b6ed..c1dfca561c3 100644 --- a/examples/microprofile/static-content/pom.xml +++ b/examples/microprofile/static-content/pom.xml @@ -60,6 +60,11 @@ hamcrest-all test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/http/tests/encoding/deflate/pom.xml b/http/tests/encoding/deflate/pom.xml index 223b841c266..9485a7f5c7b 100644 --- a/http/tests/encoding/deflate/pom.xml +++ b/http/tests/encoding/deflate/pom.xml @@ -60,5 +60,10 @@ hamcrest-all test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/http/tests/encoding/gzip/pom.xml b/http/tests/encoding/gzip/pom.xml index e7d73b704b4..2c5c0d387fc 100644 --- a/http/tests/encoding/gzip/pom.xml +++ b/http/tests/encoding/gzip/pom.xml @@ -65,5 +65,10 @@ helidon-webserver-static-content test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/integrations/micrometer/cdi/pom.xml b/integrations/micrometer/cdi/pom.xml index b1429cfabe3..c3ca6becc0b 100644 --- a/integrations/micrometer/cdi/pom.xml +++ b/integrations/micrometer/cdi/pom.xml @@ -108,6 +108,11 @@ helidon-common-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/health/pom.xml b/microprofile/health/pom.xml index d4e7b3362a4..e504fe15795 100644 --- a/microprofile/health/pom.xml +++ b/microprofile/health/pom.xml @@ -80,6 +80,11 @@ helidon-microprofile-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/lra/jax-rs/pom.xml b/microprofile/lra/jax-rs/pom.xml index 21a0b9fb708..a463007a48e 100644 --- a/microprofile/lra/jax-rs/pom.xml +++ b/microprofile/lra/jax-rs/pom.xml @@ -97,6 +97,11 @@ io.helidon.webclient test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/metrics/pom.xml b/microprofile/metrics/pom.xml index 72025101774..0658cc1a4f8 100644 --- a/microprofile/metrics/pom.xml +++ b/microprofile/metrics/pom.xml @@ -103,6 +103,11 @@ helidon-common-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/security/pom.xml b/microprofile/security/pom.xml index de84ae33c25..ead9cde3743 100644 --- a/microprofile/security/pom.xml +++ b/microprofile/security/pom.xml @@ -99,6 +99,11 @@ helidon-security-providers-http-auth test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/tests/tck/tck-config/pom.xml b/microprofile/tests/tck/tck-config/pom.xml index d230939218e..5f4a3dd8ba2 100644 --- a/microprofile/tests/tck/tck-config/pom.xml +++ b/microprofile/tests/tck/tck-config/pom.xml @@ -69,6 +69,11 @@ testng test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/tests/tck/tck-telemetry/pom.xml b/microprofile/tests/tck/tck-telemetry/pom.xml index c80bf114bbf..7cc9a288a60 100644 --- a/microprofile/tests/tck/tck-telemetry/pom.xml +++ b/microprofile/tests/tck/tck-telemetry/pom.xml @@ -76,6 +76,11 @@ testng test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/microprofile/tracing/pom.xml b/microprofile/tracing/pom.xml index cc4b911e5b8..4abedf4e277 100644 --- a/microprofile/tracing/pom.xml +++ b/microprofile/tracing/pom.xml @@ -100,6 +100,11 @@ microprofile-rest-client-api compile + + io.helidon.logging + helidon-logging-jul + test + diff --git a/security/providers/oidc-common/pom.xml b/security/providers/oidc-common/pom.xml index 24baca678d1..a2be414384a 100644 --- a/security/providers/oidc-common/pom.xml +++ b/security/providers/oidc-common/pom.xml @@ -113,6 +113,11 @@ hamcrest-core test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/security/providers/oidc/pom.xml b/security/providers/oidc/pom.xml index 19817daff75..fe634819c7e 100644 --- a/security/providers/oidc/pom.xml +++ b/security/providers/oidc/pom.xml @@ -117,6 +117,11 @@ jakarta.inject-api provided + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/context-propagation/pom.xml b/tests/functional/context-propagation/pom.xml index ca1caa7158f..1f326134ed4 100644 --- a/tests/functional/context-propagation/pom.xml +++ b/tests/functional/context-propagation/pom.xml @@ -70,5 +70,10 @@ junit-jupiter-params test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/jax-rs-multiple-apps/pom.xml b/tests/functional/jax-rs-multiple-apps/pom.xml index d02e65b8ebd..83e9cacdc95 100644 --- a/tests/functional/jax-rs-multiple-apps/pom.xml +++ b/tests/functional/jax-rs-multiple-apps/pom.xml @@ -59,5 +59,10 @@ helidon-microprofile-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/jax-rs-subresource/pom.xml b/tests/functional/jax-rs-subresource/pom.xml index 1ce66bc7b29..26fa0c56d9e 100644 --- a/tests/functional/jax-rs-subresource/pom.xml +++ b/tests/functional/jax-rs-subresource/pom.xml @@ -59,5 +59,10 @@ junit-jupiter-params test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/mp-compression/pom.xml b/tests/functional/mp-compression/pom.xml index 6930d5c769f..14553630d98 100644 --- a/tests/functional/mp-compression/pom.xml +++ b/tests/functional/mp-compression/pom.xml @@ -52,5 +52,10 @@ helidon-microprofile-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/request-scope-cdi/pom.xml b/tests/functional/request-scope-cdi/pom.xml index b41855dbfab..70c44401279 100644 --- a/tests/functional/request-scope-cdi/pom.xml +++ b/tests/functional/request-scope-cdi/pom.xml @@ -59,5 +59,10 @@ helidon-microprofile-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/request-scope-injection/pom.xml b/tests/functional/request-scope-injection/pom.xml index d319fbfc610..87108448c12 100644 --- a/tests/functional/request-scope-injection/pom.xml +++ b/tests/functional/request-scope-injection/pom.xml @@ -57,5 +57,10 @@ junit-jupiter-params test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/functional/request-scope/pom.xml b/tests/functional/request-scope/pom.xml index a954602a778..dc97e94e903 100644 --- a/tests/functional/request-scope/pom.xml +++ b/tests/functional/request-scope/pom.xml @@ -59,5 +59,10 @@ helidon-microprofile-testing-junit5 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/integration/jms/pom.xml b/tests/integration/jms/pom.xml index b57db082739..5607dc7b3b4 100644 --- a/tests/integration/jms/pom.xml +++ b/tests/integration/jms/pom.xml @@ -78,5 +78,10 @@ slf4j-jdk14 test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/integration/jpa/appl/pom.xml b/tests/integration/jpa/appl/pom.xml index cbdb2cb2c2a..7c6c69c2805 100644 --- a/tests/integration/jpa/appl/pom.xml +++ b/tests/integration/jpa/appl/pom.xml @@ -120,6 +120,11 @@ hamcrest-all test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/integration/jpa/simple/pom.xml b/tests/integration/jpa/simple/pom.xml index 4a2a47d241f..0f8331e3ede 100644 --- a/tests/integration/jpa/simple/pom.xml +++ b/tests/integration/jpa/simple/pom.xml @@ -121,6 +121,11 @@ slf4j-jdk14 runtime + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tests/integration/kafka/pom.xml b/tests/integration/kafka/pom.xml index 69cd54271b1..901bebb6ddd 100644 --- a/tests/integration/kafka/pom.xml +++ b/tests/integration/kafka/pom.xml @@ -134,5 +134,10 @@ org.slf4j slf4j-jdk14 + + io.helidon.logging + helidon-logging-jul + test + diff --git a/tracing/tests/it-tracing-client-zipkin/pom.xml b/tracing/tests/it-tracing-client-zipkin/pom.xml index 12198ea036a..c96ae79683c 100644 --- a/tracing/tests/it-tracing-client-zipkin/pom.xml +++ b/tracing/tests/it-tracing-client-zipkin/pom.xml @@ -72,5 +72,10 @@ mockito-core test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/webclient/tests/webclient/pom.xml b/webclient/tests/webclient/pom.xml index 9eb0e73d633..855dc1e41b4 100644 --- a/webclient/tests/webclient/pom.xml +++ b/webclient/tests/webclient/pom.xml @@ -119,5 +119,10 @@ opentracing-mock test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/webserver/testing/junit5/websocket/pom.xml b/webserver/testing/junit5/websocket/pom.xml index 83faec38b9b..0eb67b59e54 100644 --- a/webserver/testing/junit5/websocket/pom.xml +++ b/webserver/testing/junit5/websocket/pom.xml @@ -52,5 +52,10 @@ hamcrest-all provided + + io.helidon.logging + helidon-logging-jul + test + diff --git a/webserver/tests/grpc/pom.xml b/webserver/tests/grpc/pom.xml index 5dde2e4d8a6..4df3e1137f1 100644 --- a/webserver/tests/grpc/pom.xml +++ b/webserver/tests/grpc/pom.xml @@ -65,6 +65,11 @@ grpc-netty test + + io.helidon.logging + helidon-logging-jul + test + diff --git a/webserver/tests/resource-limits/pom.xml b/webserver/tests/resource-limits/pom.xml index e30549708be..17ea031f950 100644 --- a/webserver/tests/resource-limits/pom.xml +++ b/webserver/tests/resource-limits/pom.xml @@ -77,5 +77,10 @@ hamcrest-all test + + io.helidon.logging + helidon-logging-jul + test + \ No newline at end of file From 335c98b7ed40d167a21e39e554e5d8ed4deab364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Tue, 11 Jun 2024 19:20:32 +0200 Subject: [PATCH 050/245] Jwt improvements (#8865) Signed-off-by: David Kral --- etc/checkstyle-suppressions.xml | 4 + .../jwt/auth/JwtAuthProvider.java | 27 +- .../security/jwt/AudienceValidator.java | 100 ++++ .../helidon/security/jwt/ClaimValidator.java | 53 +++ .../helidon/security/jwt/CommonValidator.java | 112 +++++ .../security/jwt/CriticalValidator.java | 155 +++++++ .../io/helidon/security/jwt/EncryptedJwt.java | 5 +- .../security/jwt/ExpirationValidator.java | 76 ++++ .../helidon/security/jwt/FieldValidator.java | 152 +++++++ .../security/jwt/InstantValidator.java | 85 ++++ .../security/jwt/IssueTimeValidator.java | 69 +++ .../java/io/helidon/security/jwt/Jwt.java | 272 +++++++---- .../io/helidon/security/jwt/JwtHeaders.java | 119 ++++- .../io/helidon/security/jwt/JwtScope.java | 34 ++ .../java/io/helidon/security/jwt/JwtUtil.java | 8 +- .../io/helidon/security/jwt/JwtValidator.java | 430 ++++++++++++++++++ .../helidon/security/jwt/JwtValidators.java | 41 ++ .../security/jwt/MaxTokenAgeValidator.java | 97 ++++ .../security/jwt/NotBeforeValidator.java | 68 +++ .../security/jwt/OptionalValidator.java | 77 ++++ .../security/jwt/UserPrincipalValidator.java | 61 +++ .../security/jwt/ValidatorWrapper.java | 81 ++++ .../io/helidon/security/jwt/jwk/JwkOctet.java | 5 +- .../java/io/helidon/security/jwt/JwtTest.java | 23 +- .../security/jwt/JwtValidatorTest.java | 112 +++++ .../mapper/IdcsRoleMapperProviderBase.java | 8 +- .../security/providers/jwt/JwtProvider.java | 11 +- .../oidc/TenantAuthenticationHandler.java | 37 +- 28 files changed, 2172 insertions(+), 150 deletions(-) create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/AudienceValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/ClaimValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/CommonValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/CriticalValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/ExpirationValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/FieldValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/InstantValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/IssueTimeValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/JwtScope.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/JwtValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/JwtValidators.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/MaxTokenAgeValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/NotBeforeValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/OptionalValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/UserPrincipalValidator.java create mode 100644 security/jwt/src/main/java/io/helidon/security/jwt/ValidatorWrapper.java create mode 100644 security/jwt/src/test/java/io/helidon/security/jwt/JwtValidatorTest.java diff --git a/etc/checkstyle-suppressions.xml b/etc/checkstyle-suppressions.xml index e3af4e8b4bf..4bd5e4208af 100644 --- a/etc/checkstyle-suppressions.xml +++ b/etc/checkstyle-suppressions.xml @@ -43,6 +43,10 @@ record here. + + + diff --git a/microprofile/jwt-auth/src/main/java/io/helidon/microprofile/jwt/auth/JwtAuthProvider.java b/microprofile/jwt-auth/src/main/java/io/helidon/microprofile/jwt/auth/JwtAuthProvider.java index c021515eaf4..194e8979081 100644 --- a/microprofile/jwt-auth/src/main/java/io/helidon/microprofile/jwt/auth/JwtAuthProvider.java +++ b/microprofile/jwt-auth/src/main/java/io/helidon/microprofile/jwt/auth/JwtAuthProvider.java @@ -73,6 +73,7 @@ import io.helidon.security.jwt.Jwt; import io.helidon.security.jwt.JwtException; import io.helidon.security.jwt.JwtHeaders; +import io.helidon.security.jwt.JwtValidator; import io.helidon.security.jwt.SignedJwt; import io.helidon.security.jwt.Validator; import io.helidon.security.jwt.jwk.Jwk; @@ -278,27 +279,27 @@ AuthenticationResponse authenticate(ProviderRequest providerRequest, LoginConfig Errors errors = signedJwt.verifySignature(verifyKeys.get(), defaultJwk.get()); if (errors.isValid()) { Jwt jwt = signedJwt.getJwt(); - - List> validators = new LinkedList<>(); + JwtValidator.Builder valBuilder = JwtValidator.builder(); if (expectedIssuer != null) { // validate issuer - Jwt.addIssuerValidator(validators, expectedIssuer, true); + valBuilder.addIssuerValidator(expectedIssuer); } - if (expectedAudiences.size() > 0) { + if (!expectedAudiences.isEmpty()) { // validate audience(s) - Jwt.addAudienceValidator(validators, expectedAudiences, true); + valBuilder.addAudienceValidator(expectedAudiences); } // validate user principal is present - Jwt.addUserPrincipalValidator(validators); - validators.add(Jwt.ExpirationValidator.create(Instant.now(), - (int) clockSkew.getSeconds(), - ChronoUnit.SECONDS, - true)); + valBuilder.addUserPrincipalValidator() + .addExpirationValidator(builder -> builder.now(Instant.now()) + .allowedTimeSkew(clockSkew) + .mandatory(true)); if (expectedMaxTokenAge != null) { - Jwt.addMaxTokenAgeValidator(validators, expectedMaxTokenAge, clockSkew, true); + valBuilder.addMaxTokenAgeValidator(builder -> builder.expectedMaxTokenAge(expectedMaxTokenAge) + .allowedTimeSkew(clockSkew) + .mandatory(true)); } - - Errors validate = jwt.validate(validators); + JwtValidator jwtValidator = valBuilder.build(); + Errors validate = jwtValidator.validate(jwt); if (validate.isValid()) { return AuthenticationResponse.success(buildSubject(jwt, signedJwt)); diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/AudienceValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/AudienceValidator.java new file mode 100644 index 00000000000..bfeff049532 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/AudienceValidator.java @@ -0,0 +1,100 @@ +/* + * 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.security.jwt; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +import io.helidon.common.Errors; + +/** + * Audience claim validator. + */ +public final class AudienceValidator extends OptionalValidator { + private final Set expectedAudience; + + private AudienceValidator(Builder builder) { + super(builder); + this.expectedAudience = Set.copyOf(builder.expectedAudience); + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder() + .addClaim(Jwt.AUDIENCE) + .mandatory(true); + } + + @Override + public void validate(Jwt jwt, Errors.Collector collector, List validators) { + Optional> jwtAudiences = jwt.audience(); + jwtAudiences.ifPresent(jwtAudience -> { + if (expectedAudience.stream().anyMatch(jwtAudiences.get()::contains)) { + return; + } + collector.fatal(jwt, "Audience must contain " + expectedAudience + ", yet it is: " + jwtAudiences); + }); + super.validate(Jwt.AUDIENCE, jwtAudiences, collector); + } + + /** + * Builder of the {@link AudienceValidator}. + */ + public static final class Builder extends OptionalValidator.BaseBuilder { + + private Set expectedAudience = new HashSet<>(); + + private Builder() { + } + + @Override + public AudienceValidator build() { + return new AudienceValidator(this); + } + + /** + * Add expected audience value. + * + * @param audience expected audience + * @return updated builder instance + */ + public Builder addExpectedAudience(String audience) { + Objects.requireNonNull(audience); + expectedAudience.add(audience); + return this; + } + + /** + * Overwrite previously set audience with the new {@link Set} of values. + * + * @param expectedAudience expected audience values + * @return updated builder instance + */ + public Builder expectedAudience(Set expectedAudience) { + Objects.requireNonNull(expectedAudience); + this.expectedAudience = new HashSet<>(expectedAudience); + return this; + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/ClaimValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/ClaimValidator.java new file mode 100644 index 00000000000..46264818382 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/ClaimValidator.java @@ -0,0 +1,53 @@ +/* + * 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.security.jwt; + +import java.util.List; +import java.util.Set; + +import io.helidon.common.Errors; + +/** + * JWT claim validator. + */ +public interface ClaimValidator { + + /** + * Scope of the JWT claims. + * + * @return JWT claim scope + */ + JwtScope jwtScope(); + + /** + * Handled JWT claims. + * + * @return claims + */ + Set claims(); + + /** + * Validate JWT against this class's configuration. + * + * @param jwt jwt to validate + * @param collector collector of error messages to add problems to. Use {@link Errors.Collector#fatal(Object, String)} + * to mark the validation as a failure + * @param validators immutable list of all currently processed claim validators + */ + void validate(Jwt jwt, Errors.Collector collector, List validators); + +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/CommonValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/CommonValidator.java new file mode 100644 index 00000000000..a70ebd26357 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/CommonValidator.java @@ -0,0 +1,112 @@ +/* + * 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.security.jwt; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +abstract class CommonValidator implements ClaimValidator { + + private final JwtScope scope; + private final Set claims; + + CommonValidator(BaseBuilder builder) { + this.scope = builder.scope; + this.claims = Set.copyOf(builder.claims); + } + + @Override + public JwtScope jwtScope() { + return scope; + } + + @Override + public Set claims() { + return claims; + } + + abstract static class BaseBuilder, T> + implements io.helidon.common.Builder, T> { + + private JwtScope scope = JwtScope.PAYLOAD; + private Set claims = new HashSet<>(); + + BaseBuilder() { + } + + /** + * The scope of JWT. + * Default value is {@link JwtScope#PAYLOAD}. + * + * @param scope jwt scope + * @return updated builder instance + */ + B scope(JwtScope scope) { + this.scope = Objects.requireNonNull(scope); + return me(); + } + + /** + * Add JWT claim this validator is bound to. + * + * @param claim claim name + * @return updated builder instance + */ + B addClaim(String claim) { + Objects.requireNonNull(claim); + this.claims.add(claim); + return me(); + } + + /** + * Add JWT claim this validator is bound to. + * + * @param claims bound claims + * @return updated builder instance + */ + B claims(Set claims) { + Objects.requireNonNull(claims); + this.claims = new HashSet<>(claims); + return me(); + } + + /** + * Clear all set claims. + * + * @return updated builder instance + */ + B clearClaims() { + this.claims.clear(); + return me(); + } + + /** + * Currently set {@link JwtScope} scope value. + * + * @return scope value + */ + JwtScope scope() { + return scope; + } + + @SuppressWarnings("unchecked") + protected B me() { + return (B) this; + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/CriticalValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/CriticalValidator.java new file mode 100644 index 00000000000..df1fe5698fc --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/CriticalValidator.java @@ -0,0 +1,155 @@ +/* + * 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.security.jwt; + +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import io.helidon.common.Errors; + +final class CriticalValidator implements ClaimValidator { + + private static final Set INVALID_CRIT_HEADERS; + + static { + Set names = new HashSet<>(); + names.add(JwtHeaders.ALGORITHM); + names.add(JwtHeaders.ENCRYPTION); + names.add(JwtHeaders.TYPE); + names.add(JwtHeaders.CONTENT_TYPE); + names.add(JwtHeaders.KEY_ID); + names.add(JwtHeaders.JWK_SET_URL); + names.add(JwtHeaders.JSON_WEB_KEY); + names.add(JwtHeaders.X509_URL); + names.add(JwtHeaders.X509_CERT_CHAIN); + names.add(JwtHeaders.X509_CERT_SHA1_THUMB); + names.add(JwtHeaders.X509_CERT_SHA256_THUMB); + names.add(JwtHeaders.CRITICAL); + names.add(JwtHeaders.COMPRESSION_ALGORITHM); + names.add(JwtHeaders.AGREEMENT_PARTYUINFO); + names.add(JwtHeaders.AGREEMENT_PARTYVINFO); + names.add(JwtHeaders.EPHEMERAL_PUBLIC_KEY); + + INVALID_CRIT_HEADERS = Set.copyOf(names); + } + + @Override + public JwtScope jwtScope() { + return JwtScope.HEADER; + } + + @Override + public Set claims() { + return Set.of(Jwt.CRITICAL); + } + + // Taken from RFC7515 - 4.1.11 "crit" (Critical) Header Parameter + // + // The "crit" (critical) Header Parameter indicates that extensions to + // this specification and/or [JWA] are being used that MUST be + // understood and processed. Its value is an array listing the Header + // Parameter names present in the JOSE Header that use those extensions. + // If any of the listed extension Header Parameters are not understood + // and supported by the recipient, then the JWS is invalid. Producers + // MUST NOT include Header Parameter names defined by this specification + // or [JWA] for use with JWS, duplicate names, or names that do not + // occur as Header Parameter names within the JOSE Header in the "crit" + // list. Producers MUST NOT use the empty list "[]" as the "crit" + // value. Recipients MAY consider the JWS to be invalid if the critical + // list contains any Header Parameter names defined by this + // specification or [JWA] for use with JWS or if any other constraints + // on its use are violated. When used, this Header Parameter MUST be + // integrity protected; therefore, it MUST occur only within the JWS + // Protected Header. Use of this Header Parameter is OPTIONAL. This + // Header Parameter MUST be understood and processed by implementations. + // + // An example use, along with a hypothetical "exp" (expiration time) + // field is: + // + // {"alg":"ES256", + // "crit":["exp"], + // "exp":1363284000 + // } + @Override + public void validate(Jwt jwt, Errors.Collector collector, List validators) { + Optional> maybeCritical = jwt.headers().critical(); + if (maybeCritical.isPresent()) { + List critical = maybeCritical.get(); + if (critical.isEmpty()) { + collector.fatal(jwt, "JWT critical header must not be empty"); + return; + } + checkAllCriticalAvailable(jwt, critical, collector); + if (collector.hasFatal()) { + return; + } + checkDuplicity(jwt, critical, collector); + if (collector.hasFatal()) { + return; + } + checkInvalidHeaders(jwt, critical, collector); + if (collector.hasFatal()) { + return; + } + checkNotSupportedHeaders(jwt, critical, collector, validators); + } + } + + private void checkAllCriticalAvailable(Jwt jwt, List critical, Errors.Collector collector) { + Set headerClaims = jwt.headers().headerClaims().keySet(); + boolean containsAllCritical = headerClaims.containsAll(critical); + if (!containsAllCritical) { + collector.fatal(jwt, "JWT must contain " + critical + ", yet it contains: " + headerClaims); + } + } + + private void checkNotSupportedHeaders(Jwt jwt, + List critical, + Errors.Collector collector, + List validators) { + Set supportedHeaderClaims = validators + .stream() + .filter(claimValidator -> claimValidator.jwtScope() == JwtScope.HEADER) + .map(ClaimValidator::claims) + .flatMap(Set::stream) + .collect(Collectors.toSet()); + boolean containUnsupported = supportedHeaderClaims.containsAll(critical); + if (!containUnsupported) { + collector.fatal(jwt, "JWT is required to process " + critical + + ", yet it process only " + supportedHeaderClaims); + } + } + + private void checkDuplicity(Jwt jwt, List critical, Errors.Collector collector) { + Set copy = new HashSet<>(critical); + if (copy.size() != critical.size()) { + collector.fatal(jwt, "JWT critical header contains duplicated values: " + critical); + } + } + + private void checkInvalidHeaders(Jwt jwt, List critical, Errors.Collector collector) { + for (String header : critical) { + if (INVALID_CRIT_HEADERS.contains(header)) { + collector.fatal(jwt, "Required critical header value '" + header + "' is invalid. " + + "This required header is defined among JWA, JWE or JWS headers."); + } + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/EncryptedJwt.java b/security/jwt/src/main/java/io/helidon/security/jwt/EncryptedJwt.java index 2806c58f599..faecd9b632d 100644 --- a/security/jwt/src/main/java/io/helidon/security/jwt/EncryptedJwt.java +++ b/security/jwt/src/main/java/io/helidon/security/jwt/EncryptedJwt.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. @@ -18,6 +18,7 @@ import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; @@ -795,7 +796,7 @@ private boolean verifySignature(EncryptionParts encryptionParts) { mac.update(encryptionParts.aad()); mac.update(encryptionParts.encryptedContent()); byte[] authKey = mac.doFinal(); - return Arrays.equals(authKey, encryptionParts.authTag()); + return MessageDigest.isEqual(authKey, encryptionParts.authTag()); } catch (InvalidKeyException e) { throw new JwtException("Exception occurred while HMAC signature."); } diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/ExpirationValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/ExpirationValidator.java new file mode 100644 index 00000000000..8df953af32d --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/ExpirationValidator.java @@ -0,0 +1,76 @@ +/* + * 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.security.jwt; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import io.helidon.common.Errors; + +/** + * Validator of expiration claim. + */ +public final class ExpirationValidator extends InstantValidator { + + private ExpirationValidator(Builder builder) { + super(builder); + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder() + .addClaim(Jwt.EXPIRATION) + .addClaim(Jwt.ISSUED_AT); + } + + @Override + public void validate(Jwt token, Errors.Collector collector, List validators) { + Optional expirationTime = token.expirationTime(); + expirationTime.ifPresent(it -> { + if (earliest().isAfter(it)) { + collector.fatal(token, "Token no longer valid, expiration: " + it); + } + token.issueTime().ifPresent(issued -> { + if (issued.isAfter(it)) { + collector.fatal(token, "Token issue date is after its expiration, " + + "issue: " + it + ", expiration: " + it); + } + }); + }); + // ensure we fail if mandatory and not present + super.validate("expirationTime", expirationTime, collector); + } + + /** + * Builder of the {@link ExpirationValidator}. + */ + public static final class Builder extends InstantValidator.BaseBuilder { + + private Builder() { + } + + @Override + public ExpirationValidator build() { + return new ExpirationValidator(this); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/FieldValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/FieldValidator.java new file mode 100644 index 00000000000..5e76d3a0779 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/FieldValidator.java @@ -0,0 +1,152 @@ +/* + * 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.security.jwt; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +import io.helidon.common.Errors; + +import jakarta.json.JsonString; + +/** + * Validator of a string field obtained from the JWT. + */ +public final class FieldValidator extends OptionalValidator { + private final Function> fieldAccessor; + private final String name; + private final String expectedValue; + + private FieldValidator(Builder builder) { + super(builder); + this.name = builder.name; + this.fieldAccessor = builder.fieldAccessor; + this.expectedValue = builder.expectedValue; + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); + } + + @Override + public void validate(Jwt token, Errors.Collector collector, List validators) { + super.validate(name, fieldAccessor.apply(token), collector) + .ifPresent(it -> { + if (!expectedValue.equals(it)) { + collector.fatal(token, + "Expected value of field \"" + name + "\" was \"" + expectedValue + "\", but " + + "actual value is: \"" + it + "\""); + } + }); + } + + /** + * Builder of the {@link FieldValidator}. + */ + public static final class Builder extends OptionalValidator.BaseBuilder { + + private String claimKey; + private String name; + private String expectedValue; + private Function> fieldAccessor; + + private Builder() { + } + + /** + * Set handled claim key. + * + * @param claimKey supported claim key + * @return updated builder instance + */ + public Builder claimKey(String claimKey) { + //This supports only one claim name + clearClaims(); + addClaim(claimKey); + this.claimKey = claimKey; + return this; + } + + /** + * Field name value. + * + * @param name name of the field + * @return updated builder instance + */ + public Builder name(String name) { + this.name = Objects.requireNonNull(name); + return this; + } + + /** + * Expected value to be present in the supported claim. + * + * @param expectedValue expected claim value + * @return updated builder instance + */ + public Builder expectedValue(String expectedValue) { + this.expectedValue = Objects.requireNonNull(expectedValue); + return this; + } + + /** + * Function to extract field from JWT. + * + * @param fieldAccessor function to extract field from JWT + * @return updated builder instance + */ + public Builder fieldAccessor(Function> fieldAccessor) { + this.fieldAccessor = fieldAccessor; + return this; + } + + @Override + public Builder scope(JwtScope scope) { + return super.scope(scope); + } + + @Override + public FieldValidator build() { + Errors.Collector collector = Errors.collector(); + if (name == null) { + collector.fatal(getClass(), "Missing supported field name"); + } + if (expectedValue == null) { + collector.fatal(getClass(), "Field accessor or claim key name has to be set."); + } + if (fieldAccessor == null) { + if (claimKey == null) { + collector.fatal(getClass(), "Field accessor or claim key name has to be set."); + } + if (scope() == JwtScope.PAYLOAD) { + fieldAccessor = jwt -> jwt.payloadClaim(claimKey).map(it -> ((JsonString) it).getString()); + } else { + fieldAccessor = jwt -> jwt.headerClaim(claimKey).map(it -> ((JsonString) it).getString()); + } + } + collector.collect().checkValid(); + return new FieldValidator(this); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/InstantValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/InstantValidator.java new file mode 100644 index 00000000000..41722a222a3 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/InstantValidator.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.security.jwt; + +import java.time.Duration; +import java.time.Instant; +import java.util.Objects; + +abstract class InstantValidator extends OptionalValidator { + private final Instant now; + private final Duration allowedTimeSkew; + + InstantValidator(BaseBuilder builder) { + super(builder); + this.now = builder.now; + this.allowedTimeSkew = builder.allowedTimeSkew; + } + + Instant latest() { + return instant().plus(allowedTimeSkew); + } + + Instant latest(Instant now) { + return now.plus(allowedTimeSkew); + } + + Instant earliest() { + return instant().minus(allowedTimeSkew); + } + + Instant earliest(Instant now) { + return now.minus(allowedTimeSkew); + } + + Instant instant() { + return now == null ? Instant.now() : now; + } + + abstract static class BaseBuilder, T> extends OptionalValidator.BaseBuilder { + + private Instant now = null; + private Duration allowedTimeSkew = Duration.ofSeconds(5); + + BaseBuilder() { + } + + /** + * Specific "current" time to validate time claim against. + * If not set, {@link Instant#now()} is used for every validation again. + * + * @param now specific current time + * @return updated builder instance + */ + public B now(Instant now) { + this.now = Objects.requireNonNull(now); + return me(); + } + + /** + * Allowed time skew for time validation. + * The default value is 5 seconds. + * + * @param allowedTimeSkew allowed time skew + * @return updated builder instance + */ + public B allowedTimeSkew(Duration allowedTimeSkew) { + this.allowedTimeSkew = Objects.requireNonNull(allowedTimeSkew); + return me(); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/IssueTimeValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/IssueTimeValidator.java new file mode 100644 index 00000000000..2864287a1ba --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/IssueTimeValidator.java @@ -0,0 +1,69 @@ +/* + * 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.security.jwt; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import io.helidon.common.Errors; + +/** + * Validator of the issue time claim. + */ +public final class IssueTimeValidator extends InstantValidator { + + private IssueTimeValidator(Builder builder) { + super(builder); + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder().addClaim(Jwt.ISSUED_AT); + } + + @Override + public void validate(Jwt token, Errors.Collector collector, List validators) { + Optional issueTime = token.issueTime(); + issueTime.ifPresent(it -> { + // must be issued in the past + if (latest().isBefore(it)) { + collector.fatal(token, "Token was not issued in the past: " + it); + } + }); + // ensure we fail if mandatory and not present + super.validate("issueTime", issueTime, collector); + } + + /** + * Builder of the {@link IssueTimeValidator}. + */ + public static final class Builder extends InstantValidator.BaseBuilder { + + private Builder() { + } + + @Override + public IssueTimeValidator build() { + return new IssueTimeValidator(this); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java b/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java index 4d9a799bf38..b3e5a7e4b32 100644 --- a/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java +++ b/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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,6 +33,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Function; import io.helidon.common.Errors; @@ -55,6 +56,41 @@ @SuppressWarnings("WeakerAccess") // getters should be public public class Jwt { + static final String CRITICAL = "crit"; + + static final String ISSUER = "iss"; + static final String SUBJECT = "sub"; + static final String AUDIENCE = "aud"; + static final String EXPIRATION = "exp"; + static final String NOT_BEFORE = "nbf"; + static final String ISSUED_AT = "iat"; + static final String USER_PRINCIPAL = "upn"; + static final String USER_GROUPS = "groups"; + static final String JWT_ID = "jti"; + static final String EMAIL = "email"; + static final String EMAIL_VERIFIED = "email_verified"; + static final String FULL_NAME = "name"; + static final String GIVEN_NAME = "given_name"; + static final String MIDDLE_NAME = "middle_name"; + static final String FAMILY_NAME = "family_name"; + static final String LOCALE = "locale"; + static final String NICKNAME = "nickname"; + static final String PREFERRED_USERNAME = "preferred_username"; + static final String PROFILE = "profile"; + static final String PICTURE = "picture"; + static final String WEBSITE = "website"; + static final String GENDER = "gender"; + static final String BIRTHDAY = "birthday"; + static final String ZONE_INFO = "zoneinfo"; + static final String PHONE_NUMBER = "phone_number"; + static final String PHONE_NUMBER_VERIFIED = "phone_number_verified"; + static final String UPDATED_AT = "updated_at"; + static final String ADDRESS = "address"; + static final String AT_HASH = "at_hash"; + static final String C_HASH = "c_hash"; + static final String NONCE = "nonce"; + static final String SCOPE = "scope"; + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); /* @@ -227,51 +263,51 @@ public class Jwt { this.payloadClaims = getClaims(payloadJson); // known payload - this.issuer = JwtUtil.getString(payloadJson, "iss"); - this.expirationTime = JwtUtil.toInstant(payloadJson, "exp"); - this.issueTime = JwtUtil.toInstant(payloadJson, "iat"); - this.notBefore = JwtUtil.toInstant(payloadJson, "nbf"); - this.subject = JwtUtil.getString(payloadJson, "sub"); - JsonValue groups = payloadJson.get("groups"); + this.issuer = JwtUtil.getString(payloadJson, ISSUER); + this.expirationTime = JwtUtil.toInstant(payloadJson, EXPIRATION); + this.issueTime = JwtUtil.toInstant(payloadJson, ISSUED_AT); + this.notBefore = JwtUtil.toInstant(payloadJson, NOT_BEFORE); + this.subject = JwtUtil.getString(payloadJson, SUBJECT); + JsonValue groups = payloadJson.get(USER_GROUPS); if (groups instanceof JsonArray) { - this.userGroups = JwtUtil.getStrings(payloadJson, "groups"); + this.userGroups = JwtUtil.getStrings(payloadJson, USER_GROUPS); } else { - this.userGroups = JwtUtil.getString(payloadJson, "groups").map(List::of); + this.userGroups = JwtUtil.getString(payloadJson, USER_GROUPS).map(List::of); } - JsonValue aud = payloadJson.get("aud"); + JsonValue aud = payloadJson.get(AUDIENCE); // support both a single string and an array if (aud instanceof JsonArray) { - this.audience = JwtUtil.getStrings(payloadJson, "aud"); + this.audience = JwtUtil.getStrings(payloadJson, AUDIENCE); } else { - this.audience = JwtUtil.getString(payloadJson, "aud").map(List::of); - } - - this.jwtId = JwtUtil.getString(payloadJson, "jti"); - this.email = JwtUtil.getString(payloadJson, "email"); - this.emailVerified = JwtUtil.toBoolean(payloadJson, "email_verified"); - this.fullName = JwtUtil.getString(payloadJson, "name"); - this.givenName = JwtUtil.getString(payloadJson, "given_name"); - this.middleName = JwtUtil.getString(payloadJson, "middle_name"); - this.familyName = JwtUtil.getString(payloadJson, "family_name"); - this.locale = JwtUtil.toLocale(payloadJson, "locale"); - this.nickname = JwtUtil.getString(payloadJson, "nickname"); - this.preferredUsername = JwtUtil.getString(payloadJson, "preferred_username"); - this.profile = JwtUtil.toUri(payloadJson, "profile"); - this.picture = JwtUtil.toUri(payloadJson, "picture"); - this.website = JwtUtil.toUri(payloadJson, "website"); - this.gender = JwtUtil.getString(payloadJson, "gender"); - this.birthday = JwtUtil.toDate(payloadJson, "birthday"); - this.timeZone = JwtUtil.toTimeZone(payloadJson, "zoneinfo"); - this.phoneNumber = JwtUtil.getString(payloadJson, "phone_number"); - this.phoneNumberVerified = JwtUtil.toBoolean(payloadJson, "phone_number_verified"); - this.updatedAt = JwtUtil.toInstant(payloadJson, "updated_at"); - this.address = JwtUtil.toAddress(payloadJson, "address"); - this.atHash = JwtUtil.getByteArray(payloadJson, "at_hash", "at_hash value"); - this.cHash = JwtUtil.getByteArray(payloadJson, "c_hash", "c_hash value"); - this.nonce = JwtUtil.getString(payloadJson, "nonce"); + this.audience = JwtUtil.getString(payloadJson, AUDIENCE).map(List::of); + } + + this.jwtId = JwtUtil.getString(payloadJson, JWT_ID); + this.email = JwtUtil.getString(payloadJson, EMAIL); + this.emailVerified = JwtUtil.toBoolean(payloadJson, EMAIL_VERIFIED); + this.fullName = JwtUtil.getString(payloadJson, FULL_NAME); + this.givenName = JwtUtil.getString(payloadJson, GIVEN_NAME); + this.middleName = JwtUtil.getString(payloadJson, MIDDLE_NAME); + this.familyName = JwtUtil.getString(payloadJson, FAMILY_NAME); + this.locale = JwtUtil.toLocale(payloadJson, LOCALE); + this.nickname = JwtUtil.getString(payloadJson, NICKNAME); + this.preferredUsername = JwtUtil.getString(payloadJson, PREFERRED_USERNAME); + this.profile = JwtUtil.toUri(payloadJson, PROFILE); + this.picture = JwtUtil.toUri(payloadJson, PICTURE); + this.website = JwtUtil.toUri(payloadJson, WEBSITE); + this.gender = JwtUtil.getString(payloadJson, GENDER); + this.birthday = JwtUtil.toDate(payloadJson, BIRTHDAY); + this.timeZone = JwtUtil.toTimeZone(payloadJson, ZONE_INFO); + this.phoneNumber = JwtUtil.getString(payloadJson, PHONE_NUMBER); + this.phoneNumberVerified = JwtUtil.toBoolean(payloadJson, PHONE_NUMBER_VERIFIED); + this.updatedAt = JwtUtil.toInstant(payloadJson, UPDATED_AT); + this.address = JwtUtil.toAddress(payloadJson, ADDRESS); + this.atHash = JwtUtil.getByteArray(payloadJson, AT_HASH, "at_hash value"); + this.cHash = JwtUtil.getByteArray(payloadJson, C_HASH, "c_hash value"); + this.nonce = JwtUtil.getString(payloadJson, NONCE); this.scopes = JwtUtil.toScopes(payloadJson); - this.userPrincipal = JwtUtil.getString(payloadJson, "upn") + this.userPrincipal = JwtUtil.getString(payloadJson, USER_PRINCIPAL) .or(() -> preferredUsername) .or(() -> subject); } @@ -289,29 +325,29 @@ private Jwt(Builder builder) { this.expirationTime = builder.expirationTime; this.issueTime = builder.issueTime; this.notBefore = builder.notBefore; - this.subject = builder.subject.or(() -> toOptionalString(builder.payloadClaims, "sub")); + this.subject = builder.subject.or(() -> toOptionalString(builder.payloadClaims, SUBJECT)); this.audience = Optional.ofNullable(builder.audience); this.jwtId = builder.jwtId; - this.email = builder.email.or(() -> toOptionalString(builder.payloadClaims, "email")); - this.emailVerified = builder.emailVerified.or(() -> getClaim(builder.payloadClaims, "email_verified")); - this.fullName = builder.fullName.or(() -> toOptionalString(builder.payloadClaims, "name")); - this.givenName = builder.givenName.or(() -> toOptionalString(builder.payloadClaims, "given_name")); - this.middleName = builder.middleName.or(() -> toOptionalString(builder.payloadClaims, "middle_name")); - this.familyName = builder.familyName.or(() -> toOptionalString(builder.payloadClaims, "family_name")); - this.locale = builder.locale.or(() -> getClaim(builder.payloadClaims, "locale")); - this.nickname = builder.nickname.or(() -> toOptionalString(builder.payloadClaims, "nickname")); + this.email = builder.email.or(() -> toOptionalString(builder.payloadClaims, EMAIL)); + this.emailVerified = builder.emailVerified.or(() -> getClaim(builder.payloadClaims, EMAIL_VERIFIED)); + this.fullName = builder.fullName.or(() -> toOptionalString(builder.payloadClaims, FULL_NAME)); + this.givenName = builder.givenName.or(() -> toOptionalString(builder.payloadClaims, GIVEN_NAME)); + this.middleName = builder.middleName.or(() -> toOptionalString(builder.payloadClaims, MIDDLE_NAME)); + this.familyName = builder.familyName.or(() -> toOptionalString(builder.payloadClaims, FAMILY_NAME)); + this.locale = builder.locale.or(() -> getClaim(builder.payloadClaims, LOCALE)); + this.nickname = builder.nickname.or(() -> toOptionalString(builder.payloadClaims, NICKNAME)); this.preferredUsername = builder.preferredUsername - .or(() -> toOptionalString(builder.payloadClaims, "preferred_username")); - this.profile = builder.profile.or(() -> getClaim(builder.payloadClaims, "profile")); - this.picture = builder.picture.or(() -> getClaim(builder.payloadClaims, "picture")); - this.website = builder.website.or(() -> getClaim(builder.payloadClaims, "website")); - this.gender = builder.gender.or(() -> toOptionalString(builder.payloadClaims, "gender")); - this.birthday = builder.birthday.or(() -> getClaim(builder.payloadClaims, "birthday")); - this.timeZone = builder.timeZone.or(() -> getClaim(builder.payloadClaims, "zoneinfo")); + .or(() -> toOptionalString(builder.payloadClaims, PREFERRED_USERNAME)); + this.profile = builder.profile.or(() -> getClaim(builder.payloadClaims, PROFILE)); + this.picture = builder.picture.or(() -> getClaim(builder.payloadClaims, PICTURE)); + this.website = builder.website.or(() -> getClaim(builder.payloadClaims, WEBSITE)); + this.gender = builder.gender.or(() -> toOptionalString(builder.payloadClaims, GENDER)); + this.birthday = builder.birthday.or(() -> getClaim(builder.payloadClaims, BIRTHDAY)); + this.timeZone = builder.timeZone.or(() -> getClaim(builder.payloadClaims, ZONE_INFO)); this.phoneNumber = builder.phoneNumber - .or(() -> toOptionalString(builder.payloadClaims, "phone_number")); + .or(() -> toOptionalString(builder.payloadClaims, PHONE_NUMBER)); this.phoneNumberVerified = builder.phoneNumberVerified - .or(() -> getClaim(builder.payloadClaims, "phone_number_verified")); + .or(() -> getClaim(builder.payloadClaims, PHONE_NUMBER_VERIFIED)); this.updatedAt = builder.updatedAt; this.address = builder.address; @@ -321,7 +357,7 @@ private Jwt(Builder builder) { this.scopes = builder.scopes; this.userPrincipal = builder.userPrincipal - .or(() -> toOptionalString(builder.payloadClaims, "upn")) + .or(() -> toOptionalString(builder.payloadClaims, USER_PRINCIPAL)) .or(() -> preferredUsername) .or(() -> subject); @@ -350,7 +386,9 @@ private static Optional toOptionalString(Map claims, Str * By default the time skew allowed is 5 seconds and all fields are optional. * * @return list of validators + * @deprecated use {@link JwtValidator.Builder#addDefaultTimeValidators()} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static List> defaultTimeValidators() { List> validators = new LinkedList<>(); validators.add(new ExpirationValidator(false)); @@ -369,7 +407,9 @@ public static List> defaultTimeValidators() { * @param mandatory whether the field is mandatory. True for mandatory, false for optional (for all default time * validators) * @return list of validators + * @deprecated use {@link JwtValidator.Builder#addDefaultTimeValidators(Instant, Duration, boolean)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static List> defaultTimeValidators(Instant now, int timeSkewAmount, ChronoUnit timeSkewUnit, @@ -387,7 +427,9 @@ public static List> defaultTimeValidators(Instant now, * @param validators collection of validators * @param issuer issuer expected to be in the token * @param mandatory whether issuer field is mandatory in the token (true - mandatory, false - optional) + * @deprecated use {@link JwtValidator.Builder#addIssuerValidator(String, boolean)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static void addIssuerValidator(Collection> validators, String issuer, boolean mandatory) { validators.add(FieldValidator.create(Jwt::issuer, "Issuer", issuer, mandatory)); } @@ -398,7 +440,9 @@ public static void addIssuerValidator(Collection> validators, Str * @param validators collection of validators * @param audience audience expected to be in the token, never null * @param mandatory whether the audience field is mandatory in the token + * @deprecated use {@link JwtValidator.Builder#addAudienceValidator(Consumer)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static void addAudienceValidator(Collection> validators, String audience, boolean mandatory) { addAudienceValidator(validators, Set.of(audience), mandatory); } @@ -409,7 +453,9 @@ public static void addAudienceValidator(Collection> validators, S * @param validators collection of validators * @param audience audience expected to be in the token * @param mandatory whether the audience field is mandatory in the token + * @deprecated use {@link JwtValidator.Builder#addAudienceValidator(Consumer)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static void addAudienceValidator(Collection> validators, Set audience, boolean mandatory) { validators.add((jwt, collector) -> { Optional> jwtAudiences = jwt.audience(); @@ -434,7 +480,9 @@ public static void addAudienceValidator(Collection> validators, S * @param expectedMaxTokenAge max token age since issue time * @param clockSkew clock skew * @param iatRequired whether to fail if iat clam is present + * @deprecated use {@link JwtValidator.Builder#addMaxTokenAgeValidator(Consumer)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static void addMaxTokenAgeValidator(Collection> validators, Duration expectedMaxTokenAge, Duration clockSkew, @@ -498,12 +546,10 @@ public Optional headerClaim(String claim) { public Optional payloadClaim(String claim) { JsonValue rawValue = payloadClaims.get(claim); - switch (claim) { - case "aud": + if (claim.equals(AUDIENCE)) { return Optional.ofNullable(ensureJsonArray(rawValue)); - default: - return Optional.ofNullable(rawValue); } + return Optional.ofNullable(rawValue); } private JsonValue ensureJsonArray(JsonValue rawValue) { @@ -868,49 +914,49 @@ public JsonObject payloadJson() { payloadClaims.forEach(objectBuilder::add); // known payload - this.issuer.ifPresent(it -> objectBuilder.add("iss", it)); - this.expirationTime.ifPresent(it -> objectBuilder.add("exp", it.getEpochSecond())); - this.issueTime.ifPresent(it -> objectBuilder.add("iat", it.getEpochSecond())); - this.notBefore.ifPresent(it -> objectBuilder.add("nbf", it.getEpochSecond())); - this.subject.ifPresent(it -> objectBuilder.add("sub", it)); - this.userPrincipal.ifPresent(it -> objectBuilder.add("upn", it)); + this.issuer.ifPresent(it -> objectBuilder.add(ISSUER, it)); + this.expirationTime.ifPresent(it -> objectBuilder.add(EXPIRATION, it.getEpochSecond())); + this.issueTime.ifPresent(it -> objectBuilder.add(ISSUED_AT, it.getEpochSecond())); + this.notBefore.ifPresent(it -> objectBuilder.add(NOT_BEFORE, it.getEpochSecond())); + this.subject.ifPresent(it -> objectBuilder.add(SUBJECT, it)); + this.userPrincipal.ifPresent(it -> objectBuilder.add(USER_PRINCIPAL, it)); this.userGroups.ifPresent(it -> { JsonArrayBuilder jab = JSON.createArrayBuilder(); it.forEach(jab::add); - objectBuilder.add("groups", jab); + objectBuilder.add(USER_GROUPS, jab); }); this.audience.ifPresent(it -> { JsonArrayBuilder jab = JSON.createArrayBuilder(); it.forEach(jab::add); - objectBuilder.add("aud", jab); + objectBuilder.add(AUDIENCE, jab); }); - this.jwtId.ifPresent(it -> objectBuilder.add("jti", it)); - this.email.ifPresent(it -> objectBuilder.add("email", it)); - this.emailVerified.ifPresent(it -> objectBuilder.add("email_verified", it)); - this.fullName.ifPresent(it -> objectBuilder.add("name", it)); - this.givenName.ifPresent(it -> objectBuilder.add("given_name", it)); - this.middleName.ifPresent(it -> objectBuilder.add("middle_name", it)); - this.familyName.ifPresent(it -> objectBuilder.add("family_name", it)); - this.locale.ifPresent(it -> objectBuilder.add("locale", it.toLanguageTag())); - this.nickname.ifPresent(it -> objectBuilder.add("nickname", it)); - this.preferredUsername.ifPresent(it -> objectBuilder.add("preferred_username", it)); - this.profile.ifPresent(it -> objectBuilder.add("profile", it.toASCIIString())); - this.picture.ifPresent(it -> objectBuilder.add("picture", it.toASCIIString())); - this.website.ifPresent(it -> objectBuilder.add("website", it.toASCIIString())); - this.gender.ifPresent(it -> objectBuilder.add("gender", it)); - this.birthday.ifPresent(it -> objectBuilder.add("birthday", JwtUtil.toDate(it))); - this.timeZone.ifPresent(it -> objectBuilder.add("zoneinfo", it.getId())); - this.phoneNumber.ifPresent(it -> objectBuilder.add("phone_number", it)); - this.phoneNumberVerified.ifPresent(it -> objectBuilder.add("phone_number_verified", it)); - this.updatedAt.ifPresent(it -> objectBuilder.add("updated_at", it.getEpochSecond())); - this.address.ifPresent(it -> objectBuilder.add("address", it.getJson())); - this.atHash.ifPresent(it -> objectBuilder.add("at_hash", JwtUtil.base64Url(it))); - this.cHash.ifPresent(it -> objectBuilder.add("c_hash", JwtUtil.base64Url(it))); - this.nonce.ifPresent(it -> objectBuilder.add("nonce", it)); + this.jwtId.ifPresent(it -> objectBuilder.add(JWT_ID, it)); + this.email.ifPresent(it -> objectBuilder.add(EMAIL, it)); + this.emailVerified.ifPresent(it -> objectBuilder.add(EMAIL_VERIFIED, it)); + this.fullName.ifPresent(it -> objectBuilder.add(FULL_NAME, it)); + this.givenName.ifPresent(it -> objectBuilder.add(GIVEN_NAME, it)); + this.middleName.ifPresent(it -> objectBuilder.add(MIDDLE_NAME, it)); + this.familyName.ifPresent(it -> objectBuilder.add(FAMILY_NAME, it)); + this.locale.ifPresent(it -> objectBuilder.add(LOCALE, it.toLanguageTag())); + this.nickname.ifPresent(it -> objectBuilder.add(NICKNAME, it)); + this.preferredUsername.ifPresent(it -> objectBuilder.add(PREFERRED_USERNAME, it)); + this.profile.ifPresent(it -> objectBuilder.add(PROFILE, it.toASCIIString())); + this.picture.ifPresent(it -> objectBuilder.add(PICTURE, it.toASCIIString())); + this.website.ifPresent(it -> objectBuilder.add(WEBSITE, it.toASCIIString())); + this.gender.ifPresent(it -> objectBuilder.add(GENDER, it)); + this.birthday.ifPresent(it -> objectBuilder.add(BIRTHDAY, JwtUtil.toDate(it))); + this.timeZone.ifPresent(it -> objectBuilder.add(ZONE_INFO, it.getId())); + this.phoneNumber.ifPresent(it -> objectBuilder.add(PHONE_NUMBER, it)); + this.phoneNumberVerified.ifPresent(it -> objectBuilder.add(PHONE_NUMBER_VERIFIED, it)); + this.updatedAt.ifPresent(it -> objectBuilder.add(UPDATED_AT, it.getEpochSecond())); + this.address.ifPresent(it -> objectBuilder.add(ADDRESS, it.getJson())); + this.atHash.ifPresent(it -> objectBuilder.add(AT_HASH, JwtUtil.base64Url(it))); + this.cHash.ifPresent(it -> objectBuilder.add(C_HASH, JwtUtil.base64Url(it))); + this.nonce.ifPresent(it -> objectBuilder.add(NONCE, it)); this.scopes.ifPresent(it -> { String scopesString = String.join(" ", it); - objectBuilder.add("scope", scopesString); + objectBuilder.add(SCOPE, scopesString); }); return objectBuilder.build(); @@ -918,12 +964,16 @@ public JsonObject payloadJson() { /** * Validate this JWT against provided validators. + *

    + * This method does not work properly upon validation of the {@code crit} JWT header. * * @param validators Validators to validate with. Obtain them through (e.g.) {@link #defaultTimeValidators()} * , {@link #addAudienceValidator(Collection, String, boolean)} * , {@link #addIssuerValidator(Collection, String, boolean)} * @return errors instance to check if valid and access error messages + * @deprecated use {@link JwtValidator#validate(Jwt)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public Errors validate(List> validators) { Errors.Collector collector = Errors.collector(); validators.forEach(it -> it.validate(this, collector)); @@ -946,7 +996,9 @@ public Errors validate(List> validators) { * @param audience validates that this JWT was issued for this audience. Setting this to non-null value will make * audience claim mandatory * @return errors instance to check for validation result + * @deprecated use {@link JwtValidator#validate(Jwt)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public Errors validate(String issuer, String audience) { return validate(issuer, audience == null ? Set.of() : Set.of(audience)); } @@ -961,7 +1013,9 @@ public Errors validate(String issuer, String audience) { * audience claim mandatory * @param checkAudience whether audience claim validation should be executed * @return errors instance to check for validation result + * @deprecated use {@link JwtValidator#validate(Jwt)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public Errors validate(String issuer, String audience, boolean checkAudience) { return validate(issuer, audience == null ? Set.of() : Set.of(audience), checkAudience); } @@ -983,7 +1037,9 @@ public Errors validate(String issuer, String audience, boolean checkAudience) { * any non-null value in the Set will make audience claim mandatory * @param checkAudience whether audience claim validation should be executed * @return errors instance to check for validation result + * @deprecated use {@link JwtValidator#validate(Jwt)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public Errors validate(String issuer, Set audience, boolean checkAudience) { List> validators = defaultTimeValidators(); if (null != issuer) { @@ -1012,7 +1068,9 @@ public Errors validate(String issuer, Set audience, boolean checkAudienc * @param audience validates that this JWT was issued for this audience. Setting this to non-null value and with * any non-null value in the Set will make audience claim mandatory * @return errors instance to check for validation result + * @deprecated use {@link JwtValidator#validate(Jwt)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public Errors validate(String issuer, Set audience) { return validate(issuer, audience, true); } @@ -1021,11 +1079,14 @@ public Errors validate(String issuer, Set audience) { * Adds a validator that makes sure the {@link Jwt#userPrincipal()} is present. * * @param validators validator collection to update + * @deprecated use {@link JwtValidator.Builder#addUserPrincipalValidator()} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static void addUserPrincipalValidator(Collection> validators) { validators.add(new UserPrincipalValidator()); } + @Deprecated(since = "4.0.10", forRemoval = true) private abstract static class OptionalValidator { private final boolean mandatory; @@ -1045,6 +1106,7 @@ Optional validate(String name, Optional optional, Errors.Collector col } } + @Deprecated(since = "4.0.10", forRemoval = true) private abstract static class InstantValidator extends OptionalValidator { private final Instant instant; private final long allowedTimeSkewAmount; @@ -1086,7 +1148,10 @@ Instant instant() { /** * Validator of a string field obtained from a JWT. + * + * @deprecated use {@link JwtValidator.Builder#addFieldValidator(Consumer)} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static final class FieldValidator extends OptionalValidator implements Validator { private final Function> fieldAccessor; private final String expectedValue; @@ -1220,7 +1285,10 @@ public void validate(Jwt token, Errors.Collector collector) { /** * Validator of issue time claim. + * + * @deprecated use {@link JwtValidator.Builder#addIssueTimeValidator()} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static final class IssueTimeValidator extends InstantValidator implements Validator { private IssueTimeValidator() { @@ -1272,7 +1340,10 @@ public void validate(Jwt token, Errors.Collector collector) { /** * Validator of expiration claim. + * + * @deprecated use {@link JwtValidator.Builder#addExpirationValidator()} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static final class ExpirationValidator extends InstantValidator implements Validator { private ExpirationValidator(boolean mandatory) { super(mandatory); @@ -1338,7 +1409,10 @@ public void validate(Jwt token, Errors.Collector collector) { /** * Validator of not before claim. + * + * @deprecated use {@link JwtValidator.Builder#addNotBeforeValidator()} instead */ + @Deprecated(since = "4.0.10", forRemoval = true) public static final class NotBeforeValidator extends InstantValidator implements Validator { private NotBeforeValidator() { } @@ -1918,6 +1992,17 @@ public Builder nonce(String nonce) { return this; } + /** + * Allows configuration of JWT headers directly over the {@link JwtHeaders.Builder}. + * + * @param consumer header builder consumer + * @return updated builder instance + */ + public Builder headerBuilder(Consumer consumer) { + consumer.accept(headerBuilder); + return this; + } + /** * Build and instance of the {@link Jwt}. * @@ -1940,6 +2025,7 @@ public Builder removePayloadClaim(String name) { } } + @Deprecated(since = "4.0.10", forRemoval = true) private static final class UserPrincipalValidator extends OptionalValidator implements Validator { private UserPrincipalValidator() { diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/JwtHeaders.java b/security/jwt/src/main/java/io/helidon/security/jwt/JwtHeaders.java index 662254ce3ad..733684247b9 100644 --- a/security/jwt/src/main/java/io/helidon/security/jwt/JwtHeaders.java +++ b/security/jwt/src/main/java/io/helidon/security/jwt/JwtHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 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,6 +16,7 @@ package io.helidon.security.jwt; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -47,6 +48,24 @@ * @see #parseToken(String) */ public class JwtHeaders extends JwtClaims { + + static final String ALGORITHM = "alg"; + static final String ENCRYPTION = "enc"; + static final String TYPE = "typ"; + static final String CONTENT_TYPE = "cty"; + static final String KEY_ID = "kid"; + static final String JWK_SET_URL = "jku"; + static final String JSON_WEB_KEY = "jwk"; + static final String X509_URL = "x5u"; + static final String X509_CERT_CHAIN = "x5c"; + static final String X509_CERT_SHA1_THUMB = "x5t"; + static final String X509_CERT_SHA256_THUMB = "x5t#S256"; + static final String CRITICAL = "crit"; + static final String COMPRESSION_ALGORITHM = "zip"; + static final String AGREEMENT_PARTYUINFO = "apu"; + static final String AGREEMENT_PARTYVINFO = "apv"; + static final String EPHEMERAL_PUBLIC_KEY = "epk"; + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); private final Optional algorithm; @@ -54,6 +73,7 @@ public class JwtHeaders extends JwtClaims { private final Optional contentType; private final Optional keyId; private final Optional type; + private final Optional> critical; // intended for replication into header in encrypted JWT private final Optional subject; private final Optional issuer; @@ -67,6 +87,7 @@ private JwtHeaders(Builder builder) { this.contentType = Optional.ofNullable(builder.contentType); this.keyId = Optional.ofNullable(builder.keyId); this.type = Optional.ofNullable(builder.type); + this.critical = Optional.ofNullable(builder.critical); this.subject = Optional.ofNullable(builder.subject); this.issuer = Optional.ofNullable(builder.issuer); this.audience = Optional.ofNullable(builder.audience); @@ -225,27 +246,50 @@ public Optional> audience() { return audience; } + /** + * Critical claim. + * + * @return critical claims or empty optional if not defined; list would be empty if the critical claim is defined as + * an empty array + */ + public Optional> critical() { + return critical; + } + + /** + * Return map of all header claims. + * + * @return header claims + */ + public Map headerClaims() { + return Collections.unmodifiableMap(headerClaims); + } + /** * Fluent API builder to create JWT Header. */ public static class Builder implements io.helidon.common.Builder { - private static final GenericType> STRING_LIST_TYPE = new GenericType>() { }; - - private static final Map> KNOWN_HEADER_CLAIMS; - private static final KnownField TYPE_FIELD = KnownField.create("typ", Builder::type); - private static final KnownField ALG_FIELD = KnownField.create("alg", Builder::algorithm); - private static final KnownField ENC_FIELD = KnownField.create("enc", Builder::encryption); - private static final KnownField CTY_FIELD = KnownField.create("cty", Builder::contentType); - private static final KnownField KID_FIELD = KnownField.create("kid", Builder::keyId); - private static final KnownField SUB_FIELD = KnownField.create("sub", Builder::headerSubject); + private static final GenericType> STRING_LIST_TYPE = new GenericType<>() { }; + + private static final Map> KNOWN_HEADER_CLAIMS; + private static final KnownField TYPE_FIELD = KnownField.create(TYPE, Builder::type); + private static final KnownField ALG_FIELD = KnownField.create(ALGORITHM, Builder::algorithm); + private static final KnownField ENC_FIELD = KnownField.create(ENCRYPTION, Builder::encryption); + private static final KnownField CTY_FIELD = KnownField.create(CONTENT_TYPE, Builder::contentType); + private static final KnownField KID_FIELD = KnownField.create(KEY_ID, Builder::keyId); + private static final KnownField SUB_FIELD = KnownField.create(Jwt.SUBJECT, Builder::headerSubject); private static final KnownField ISS_FIELD = KnownField.create("iss", Builder::headerIssuer); - private static final KnownField> AUD_FIELD = new KnownField>("aud", - STRING_LIST_TYPE, - Builder::headerAudience, - Builder::jsonToStringList); + private static final KnownField> CRIT_FIELD = new KnownField<>(CRITICAL, + STRING_LIST_TYPE, + Builder::headerCritical, + Builder::jsonToStringList); + private static final KnownField> AUD_FIELD = new KnownField<>("aud", + STRING_LIST_TYPE, + Builder::headerAudience, + Builder::jsonToStringList); static { - Map> map = new HashMap<>(); + Map> map = new HashMap<>(); addKnownField(map, TYPE_FIELD); addKnownField(map, ALG_FIELD); @@ -255,6 +299,7 @@ public static class Builder implements io.helidon.common.Builder audience; + private List critical; private Builder() { } @@ -279,6 +325,9 @@ public JwtHeaders build() { // this may be changing throughout the build AUD_FIELD.set(claims, audience); } + if (critical != null) { + CRIT_FIELD.set(claims, critical); + } return new JwtHeaders(this); } @@ -390,12 +439,31 @@ public Builder headerIssuer(String issuer) { return this; } + /** + * The critical claim is used to indicate that certain claims are critical and must be understood (optional). + * If a recipient does not understand or support any of the critical claims, it must reject the token. + * Multiple critical claims may be added. + * This configures critical claims in header claims. + * + * See RFC 7519, section 4.1.1. + * + * @param critical required critical claim to understand + * @return updated builder instance + */ + public Builder addHeaderCritical(String critical) { + if (this.critical == null) { + this.critical = new LinkedList<>(); + } + this.critical.add(critical); + return this; + } + /** * Audience identifies the expected recipients of this JWT (optional). * Multiple audience may be added. * This configures audience in header claims, usually this is defined in payload. * - * See RFC 7519, section 4.1.3. + * See RFC 7515, section 4.1.11. * * @param audience audience of this JWT * @return updated builder instance @@ -424,6 +492,22 @@ public Builder headerAudience(List audience) { return this; } + /** + * The critical claim is used to indicate that certain claims are critical and must be understood (optional). + * If a recipient does not understand or support any of the critical claims, it must reject the token. + * Replaces existing configured critical claims. + * This configures critical claims in header claims. + * + * See RFC 7515, section 4.1.11. + * + * @param critical required critical claims to understand + * @return updated builder instance + */ + public Builder headerCritical(List critical) { + this.critical = new ArrayList<>(critical); + return this; + } + @SuppressWarnings({"unchecked", "rawtypes"}) private void setFromGeneric(String claim, Object value) { KnownField knownField = KNOWN_HEADER_CLAIMS.get(claim); @@ -452,8 +536,7 @@ private static List jsonToStringList(JsonValue jsonValue) { throw new JwtException("Json value should have been a String or an array of Strings, but is " + jsonValue); } - private static void addKnownField(Map> map, - KnownField field) { + private static void addKnownField(Map> map, KnownField field) { map.put(field.name, field); } diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/JwtScope.java b/security/jwt/src/main/java/io/helidon/security/jwt/JwtScope.java new file mode 100644 index 00000000000..d5bef7a555f --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/JwtScope.java @@ -0,0 +1,34 @@ +/* + * 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.security.jwt; + +/** + * Scope in which JWT claims are processed. + */ +public enum JwtScope { + + /** + * Header claims. + */ + HEADER, + + /** + * Payload claims. + */ + PAYLOAD + +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/JwtUtil.java b/security/jwt/src/main/java/io/helidon/security/jwt/JwtUtil.java index 8a5ddf39722..b8841baeb63 100644 --- a/security/jwt/src/main/java/io/helidon/security/jwt/JwtUtil.java +++ b/security/jwt/src/main/java/io/helidon/security/jwt/JwtUtil.java @@ -288,7 +288,7 @@ public static JsonValue toJson(Object object) { return ((Address) object).getJson(); } if (object instanceof Collection) { - return JSON.createArrayBuilder((Collection) object).build(); + return JSON.createArrayBuilder((Collection) object).build(); } return JSON_PROVIDER.createValue(String.valueOf(object)); } @@ -310,10 +310,10 @@ static Optional

    toAddress(JsonObject json, String name) { } static Optional> toScopes(JsonObject json) { - if (json.get("scope") instanceof JsonArray) { - return getStrings(json, "scope"); + if (json.get(Jwt.SCOPE) instanceof JsonArray) { + return getStrings(json, Jwt.SCOPE); } else { - return getString(json, "scope") + return getString(json, Jwt.SCOPE) .map(it -> Arrays.asList(it.split(" "))); } } diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/JwtValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/JwtValidator.java new file mode 100644 index 00000000000..3bd0dfacc57 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/JwtValidator.java @@ -0,0 +1,430 @@ +/* + * 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.security.jwt; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +import io.helidon.common.Errors; + +/** + * Validator used for JWT claim validation. + */ +public interface JwtValidator { + + /** + * Return a new Builder of the {@link JwtValidator}. + * + * @return new builder instance + */ + static Builder builder() { + return new Builder(); + } + + /** + * Validate configured validators against provided {@link Jwt}. + * + * @param jwt JWT to validate + * @return errors instance to check if valid and access error messages + */ + Errors validate(Jwt jwt); + + /** + * Builder of the {@link JwtValidator}. + */ + final class Builder implements io.helidon.common.Builder { + + private final List claimValidators = new ArrayList<>(); + + private Builder() { + } + + @Override + public JwtValidator build() { + return new JwtValidators(this); + } + + /** + * Add {@link ClaimValidator} instance. + * + * @param claimValidator claim validator to be added + * @return updated builder instance + */ + public Builder addClaimValidator(ClaimValidator claimValidator) { + claimValidators.add(claimValidator); + return this; + } + + /** + * Add {@link Validator} instance among the claim validators. + * Default scope is set to {@link JwtScope#PAYLOAD}. + * + * @param validator to be added + * @param claims claims handled by the validator + * @return updated builder instance + */ + public Builder addValidator(Validator validator, String... claims) { + return addValidator(JwtScope.PAYLOAD, validator, claims); + } + + /** + * Add {@link Validator} instance among the claim validators. + * + * @param scope scope of the validator + * @param validator to be added + * @param claims claims handled by the validator + * @return updated builder instance + */ + public Builder addValidator(JwtScope scope, Validator validator, String... claims) { + Objects.requireNonNull(scope); + Objects.requireNonNull(validator); + claimValidators.add(ValidatorWrapper.builder() + .scope(scope) + .validator(validator) + .claims(Set.of(claims)) + .build()); + return this; + } + + /** + * Add new {@link FieldValidator} of the header field. + * + * @param claimKey claim key + * @param fieldName header field to be validated + * @param expectedValue expected value of the field + * @return updated builder instance + */ + public Builder addHeaderFieldValidator(String claimKey, String fieldName, String expectedValue) { + Objects.requireNonNull(claimKey); + Objects.requireNonNull(fieldName); + Objects.requireNonNull(expectedValue); + claimValidators.add(FieldValidator.builder() + .scope(JwtScope.HEADER) + .claimKey(claimKey) + .name(fieldName) + .expectedValue(expectedValue) + .build()); + return this; + } + + /** + * Add new {@link FieldValidator} of the payload field. + * + * @param claimKey claim key + * @param fieldName payload field to be validated + * @param expectedValue expected value of the field + * @return updated builder instance + */ + public Builder addPayloadFieldValidator(String claimKey, String fieldName, String expectedValue) { + Objects.requireNonNull(claimKey); + Objects.requireNonNull(fieldName); + Objects.requireNonNull(expectedValue); + claimValidators.add(FieldValidator.builder() + .claimKey(claimKey) + .name(fieldName) + .expectedValue(expectedValue) + .build()); + return this; + } + + /** + * Add new {@link FieldValidator} based on the builder configuration. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addFieldValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + FieldValidator.Builder builder = FieldValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Add new JWT issuer validator. + * This issuer claim is set as mandatory to be present by default. + * + * @param expectedIssuer expected JWT issuer + * @return updated builder instance + */ + public Builder addIssuerValidator(String expectedIssuer) { + return addIssuerValidator(expectedIssuer, true); + } + + /** + * Add new JWT issuer validator. + * + * @param expectedIssuer expected JWT issuer + * @param mandatory whether this issuer claim is mandatory + * @return updated builder instance + */ + public Builder addIssuerValidator(String expectedIssuer, boolean mandatory) { + Objects.requireNonNull(expectedIssuer); + claimValidators.add(FieldValidator.builder() + .fieldAccessor(Jwt::issuer) + .name("Issuer") + .expectedValue(expectedIssuer) + .mandatory(mandatory) + .build()); + return this; + } + + /** + * Add default time validators. + * This adds the following validators: + *
      + *
    • {@link ExpirationValidator}
    • + *
    • {@link IssueTimeValidator}
    • + *
    • {@link NotBeforeValidator}
    • + *
    + * + * @return updated builder instance + */ + public Builder addDefaultTimeValidators() { + addExpirationValidator(); + addIssueTimeValidator(); + addNotBeforeValidator(); + return this; + } + + /** + * Add default time validators with specific time settings. + * This adds the following validators: + *
      + *
    • {@link ExpirationValidator}
    • + *
    • {@link IssueTimeValidator}
    • + *
    • {@link NotBeforeValidator}
    • + *
    + * + * @param now time which is considered current time during the time validation + * @param allowedTimeSkew allowed time skew + * @param mandatory whether the time claims are mandatory to be present + * @return updated builder instance + */ + public Builder addDefaultTimeValidators(Instant now, Duration allowedTimeSkew, boolean mandatory) { + Objects.requireNonNull(now); + Objects.requireNonNull(allowedTimeSkew); + addExpirationValidator(builder -> builder.now(now).allowedTimeSkew(allowedTimeSkew).mandatory(mandatory)); + addIssueTimeValidator(builder -> builder.now(now).allowedTimeSkew(allowedTimeSkew).mandatory(mandatory)); + addNotBeforeValidator(builder -> builder.now(now).allowedTimeSkew(allowedTimeSkew).mandatory(mandatory)); + return this; + } + + /** + * Add new {@link ExpirationValidator} instance. + * + * @return updated builder instance + */ + public Builder addExpirationValidator() { + claimValidators.add(ExpirationValidator.builder().build()); + return this; + } + + /** + * Add new {@link ExpirationValidator} instance based on the builder configuration. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addExpirationValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + ExpirationValidator.Builder builder = ExpirationValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Add new {@link NotBeforeValidator} instance. + * + * @return updated builder instance + */ + public Builder addNotBeforeValidator() { + claimValidators.add(NotBeforeValidator.builder().build()); + return this; + } + + /** + * Add new {@link NotBeforeValidator} instance based on the builder configuration. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addNotBeforeValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + NotBeforeValidator.Builder builder = NotBeforeValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Add new {@link IssueTimeValidator} instance. + * + * @return updated builder instance + */ + public Builder addIssueTimeValidator() { + claimValidators.add(IssueTimeValidator.builder().build()); + return this; + } + + /** + * Add new {@link IssueTimeValidator} instance based on the builder configuration. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addIssueTimeValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + IssueTimeValidator.Builder builder = IssueTimeValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Add new "crit" claim validator. + * This validator behaves as mentioned in RFC 7515 - 4.1.11. + * + * @return updated builder instance + * @see RFC 7515 - 4.1.11 + */ + public Builder addCriticalValidator() { + claimValidators.add(new CriticalValidator()); + return this; + } + + /** + * Add new {@link UserPrincipalValidator}. + * This validator is mandatory by default. + * + * @return updated builder instance + */ + public Builder addUserPrincipalValidator() { + claimValidators.add(UserPrincipalValidator.builder().build()); + return this; + } + + /** + * Add new {@link UserPrincipalValidator} instance based on the builder configuration. + * This validator is mandatory by default. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addUserPrincipalValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + UserPrincipalValidator.Builder builder = UserPrincipalValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Add new {@link MaxTokenAgeValidator} with the expected max token age. + * + * @param expectedMaxTokenAge expected max token age + * @return updated builder instance + */ + public Builder addMaxTokenAgeValidator(Duration expectedMaxTokenAge) { + Objects.requireNonNull(expectedMaxTokenAge); + claimValidators.add(MaxTokenAgeValidator.builder() + .expectedMaxTokenAge(expectedMaxTokenAge) + .build()); + return this; + } + + /** + * Add new {@link MaxTokenAgeValidator} instance based on the builder configuration. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addMaxTokenAgeValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + MaxTokenAgeValidator.Builder builder = MaxTokenAgeValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Add new {@link AudienceValidator} with the expected audience. + * This validator is mandatory by default. + * + * @param expectedAudience expected audience + * @return updated builder instance + */ + public Builder addAudienceValidator(String expectedAudience) { + Objects.requireNonNull(expectedAudience); + claimValidators.add(AudienceValidator.builder() + .addExpectedAudience(expectedAudience) + .build()); + return this; + } + + /** + * Add new {@link AudienceValidator} with the expected audience. + * This validator is mandatory by default. + * + * @param expectedAudience expected audience + * @return updated builder instance + */ + public Builder addAudienceValidator(Set expectedAudience) { + Objects.requireNonNull(expectedAudience); + claimValidators.add(AudienceValidator.builder() + .expectedAudience(expectedAudience) + .build()); + return this; + } + + /** + * Add new {@link AudienceValidator} instance based on the builder configuration. + * This validator is mandatory by default. + * + * @param builderConsumer consumer of the builder + * @return updated builder instance + */ + public Builder addAudienceValidator(Consumer builderConsumer) { + Objects.requireNonNull(builderConsumer); + AudienceValidator.Builder builder = AudienceValidator.builder(); + builderConsumer.accept(builder); + claimValidators.add(builder.build()); + return this; + } + + /** + * Clear all add validators. + * + * @return updated builder instance + */ + public Builder clearValidators() { + claimValidators.clear(); + return this; + } + + List claimValidators() { + return claimValidators; + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/JwtValidators.java b/security/jwt/src/main/java/io/helidon/security/jwt/JwtValidators.java new file mode 100644 index 00000000000..fb20f314593 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/JwtValidators.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.security.jwt; + +import java.util.List; + +import io.helidon.common.Errors; + +/** + * Implementation of the JwtValidator interface. + */ +class JwtValidators implements JwtValidator { + + private final List claimValidators; + + JwtValidators(Builder builder) { + claimValidators = List.copyOf(builder.claimValidators()); + } + + @Override + public Errors validate(Jwt jwt) { + Errors.Collector collector = Errors.collector(); + claimValidators.forEach(claimValidator -> claimValidator.validate(jwt, collector, claimValidators)); + return collector.collect(); + } + +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/MaxTokenAgeValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/MaxTokenAgeValidator.java new file mode 100644 index 00000000000..8bbcfe8c6ca --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/MaxTokenAgeValidator.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.security.jwt; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import io.helidon.common.Errors; + +/** + * Max token age validator. + */ +public final class MaxTokenAgeValidator extends InstantValidator { + private final Duration expectedMaxTokenAge; + + private MaxTokenAgeValidator(Builder builder) { + super(builder); + this.expectedMaxTokenAge = builder.expectedMaxTokenAge; + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder() + .addClaim(Jwt.ISSUED_AT) + .missingClaimMessage("Claim iat is required to be present in JWT when validating token max allowed age."); + } + + @Override + public void validate(Jwt jwt, Errors.Collector collector, List validators) { + Optional maybeIssueTime = jwt.issueTime(); + maybeIssueTime.ifPresent(issueTime -> { + Instant now = instant(); + Instant earliest = earliest(issueTime); + Instant latest = latest(earliest).plus(expectedMaxTokenAge); + if (earliest.isBefore(now) && latest.isAfter(now)) { + return; + } + collector.fatal(jwt, "Current time need to be between " + earliest + + " and " + latest + ", but was " + now); + }); + super.validate(Jwt.ISSUED_AT, maybeIssueTime, collector); + } + + /** + * Builder of the {@link MaxTokenAgeValidator}. + */ + public static final class Builder extends InstantValidator.BaseBuilder { + + private Duration expectedMaxTokenAge = null; + + private Builder() { + } + + @Override + public MaxTokenAgeValidator build() { + Errors.Collector collector = Errors.collector(); + if (expectedMaxTokenAge == null) { + collector.fatal(getClass(), "Expected JWT max token age to be set"); + } + collector.collect().checkValid(); + return new MaxTokenAgeValidator(this); + } + + /** + * Expected max token age. + * + * @param expectedMaxTokenAge max token age + * @return updated builder instance + */ + public Builder expectedMaxTokenAge(Duration expectedMaxTokenAge) { + this.expectedMaxTokenAge = Objects.requireNonNull(expectedMaxTokenAge); + return this; + } + } + +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/NotBeforeValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/NotBeforeValidator.java new file mode 100644 index 00000000000..6394329a0e3 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/NotBeforeValidator.java @@ -0,0 +1,68 @@ +/* + * 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.security.jwt; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import io.helidon.common.Errors; + +/** + * Validator of not before claim. + */ +public final class NotBeforeValidator extends InstantValidator { + + private NotBeforeValidator(NotBeforeValidator.Builder builder) { + super(builder); + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder().addClaim(Jwt.NOT_BEFORE); + } + + @Override + public void validate(Jwt token, Errors.Collector collector, List validators) { + Optional notBefore = token.notBefore(); + notBefore.ifPresent(it -> { + if (latest().isBefore(it)) { + collector.fatal(token, "Token not yet valid, not before: " + it); + } + }); + // ensure we fail if mandatory and not present + super.validate("notBefore", notBefore, collector); + } + + /** + * Builder of the {@link NotBeforeValidator}. + */ + public static final class Builder extends InstantValidator.BaseBuilder { + + private Builder() { + } + + @Override + public NotBeforeValidator build() { + return new NotBeforeValidator(this); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/OptionalValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/OptionalValidator.java new file mode 100644 index 00000000000..a3854a6ca35 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/OptionalValidator.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.security.jwt; + +import java.util.Optional; + +import io.helidon.common.Errors; + +abstract class OptionalValidator extends CommonValidator { + private final boolean mandatory; + private final String missingClaimMessage; + + OptionalValidator(BaseBuilder builder) { + super(builder); + this.mandatory = builder.mandatory; + this.missingClaimMessage = builder.missingClaimMessage; + } + + Optional validate(String name, Optional optional, Errors.Collector collector) { + if (mandatory && optional.isEmpty()) { + String message; + if (missingClaimMessage == null) { + message = "Field " + name + " is mandatory, yet not defined in JWT"; + } else { + message = missingClaimMessage; + } + collector.fatal(message); + } + return optional; + } + + abstract static class BaseBuilder, T> extends CommonValidator.BaseBuilder { + + private boolean mandatory = false; + private String missingClaimMessage; + + BaseBuilder() { + } + + /** + * Whether handled claim is mandatory to be present. + * Default value is {@code false}. + * + * @param mandatory mandatory to be present + * @return updated builder instance + */ + public B mandatory(boolean mandatory) { + this.mandatory = mandatory; + return me(); + } + + /** + * Custom missing claim error message. + * + * @param missingClaimMessage missing claim error message + * @return updated builder instance + */ + public B missingClaimMessage(String missingClaimMessage) { + this.missingClaimMessage = missingClaimMessage; + return me(); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/UserPrincipalValidator.java b/security/jwt/src/main/java/io/helidon/security/jwt/UserPrincipalValidator.java new file mode 100644 index 00000000000..9c40f52488d --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/UserPrincipalValidator.java @@ -0,0 +1,61 @@ +/* + * 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.security.jwt; + +import java.util.List; + +import io.helidon.common.Errors; + +/** + * User principal validator. + */ +public final class UserPrincipalValidator extends OptionalValidator { + + private UserPrincipalValidator(Builder builder) { + super(builder); + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder() + .addClaim(Jwt.USER_PRINCIPAL) + .mandatory(true); + } + + @Override + public void validate(Jwt object, Errors.Collector collector, List validators) { + super.validate("User Principal", object.userPrincipal(), collector); + } + + /** + * Builder of the {@link UserPrincipalValidator}. + */ + public static final class Builder extends OptionalValidator.BaseBuilder { + + private Builder() { + } + + @Override + public UserPrincipalValidator build() { + return new UserPrincipalValidator(this); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/ValidatorWrapper.java b/security/jwt/src/main/java/io/helidon/security/jwt/ValidatorWrapper.java new file mode 100644 index 00000000000..65240288209 --- /dev/null +++ b/security/jwt/src/main/java/io/helidon/security/jwt/ValidatorWrapper.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.security.jwt; + +import java.util.List; +import java.util.Objects; + +import io.helidon.common.Errors; + +/** + * Wrapper support for {@link Validator} instances. + */ +final class ValidatorWrapper extends CommonValidator { + + private final Validator validator; + + private ValidatorWrapper(Builder builder) { + super(builder); + validator = builder.validator; + } + + /** + * Return a new Builder instance. + * + * @return new builder instance + */ + static Builder builder() { + return new Builder(); + } + + @Override + public void validate(Jwt jwt, Errors.Collector collector, List validators) { + validator.validate(jwt, collector); + } + + /** + * Builder of the {@link io.helidon.security.jwt.ValidatorWrapper}. + */ + static final class Builder extends BaseBuilder { + + private Validator validator; + + private Builder() { + } + + /** + * Instance of the {@link Validator}. + * + * @param validator validator instance + * @return updated builder instance + */ + public Builder validator(Validator validator) { + this.validator = Objects.requireNonNull(validator); + return this; + } + + @Override + public ValidatorWrapper build() { + Errors.Collector collector = Errors.collector(); + if (validator == null) { + collector.fatal(getClass(), "No validator instance was set"); + } + collector.collect().checkValid(); + return new ValidatorWrapper(this); + } + } +} diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/jwk/JwkOctet.java b/security/jwt/src/main/java/io/helidon/security/jwt/jwk/JwkOctet.java index 89c3a1d959c..a7e5599b579 100644 --- a/security/jwt/src/main/java/io/helidon/security/jwt/jwk/JwkOctet.java +++ b/security/jwt/src/main/java/io/helidon/security/jwt/jwk/JwkOctet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 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 @@ package io.helidon.security.jwt.jwk; import java.security.InvalidKeyException; +import java.security.MessageDigest; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -118,7 +119,7 @@ public boolean doVerify(byte[] signedBytes, byte[] signature) { } byte[] ourSignature = sign(signedBytes); - return Arrays.equals(signature, ourSignature); + return MessageDigest.isEqual(signature, ourSignature); } @Override diff --git a/security/jwt/src/test/java/io/helidon/security/jwt/JwtTest.java b/security/jwt/src/test/java/io/helidon/security/jwt/JwtTest.java index 4edde93dcad..1f5eb1ba37b 100644 --- a/security/jwt/src/test/java/io/helidon/security/jwt/JwtTest.java +++ b/security/jwt/src/test/java/io/helidon/security/jwt/JwtTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022 Oracle and/or its affiliates. + * Copyright (c) 2018, 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,6 +20,7 @@ import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import io.helidon.common.Errors; @@ -86,17 +87,27 @@ public void testOidcJwt() { assertThat(jwt.notBefore(), is(Optional.of(notBefore))); //and this one should be valid - List> vals = Jwt.defaultTimeValidators(); - Jwt.addIssuerValidator(vals, issuer, true); - Jwt.addAudienceValidator(vals, audience, true); + JwtValidator jwtValidator = JwtValidator.builder() + .addDefaultTimeValidators() + .addIssuerValidator(issuer, true) + .addAudienceValidator(audience) + .build(); - Errors errors = jwt.validate(vals); + Errors errors = jwtValidator.validate(jwt); errors.log(LOGGER); errors.checkValid(); //another try with defaults - errors = jwt.validate(issuer, audience); + jwtValidator = JwtValidator.builder() + .addDefaultTimeValidators() + .addCriticalValidator() + .addUserPrincipalValidator() + .addIssuerValidator(issuer) + .addAudienceValidator(audience) + .build(); + + errors = jwtValidator.validate(jwt); errors.log(LOGGER); errors.checkValid(); } diff --git a/security/jwt/src/test/java/io/helidon/security/jwt/JwtValidatorTest.java b/security/jwt/src/test/java/io/helidon/security/jwt/JwtValidatorTest.java new file mode 100644 index 00000000000..cda5b1ab6cd --- /dev/null +++ b/security/jwt/src/test/java/io/helidon/security/jwt/JwtValidatorTest.java @@ -0,0 +1,112 @@ +/* + * 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.security.jwt; + +import io.helidon.common.Errors; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class JwtValidatorTest { + + private static final String HEADER_CLAIM = "myTest"; + private static final String HEADER_CLAIM_VALUE = "someValue"; + + @Test + public void testCriticalValidation() { + Jwt jwt = Jwt.builder() + .headerBuilder(builder -> builder.addHeaderCritical(HEADER_CLAIM) + .addHeaderClaim(HEADER_CLAIM, HEADER_CLAIM_VALUE)) + .build(); + + JwtValidator jwtValidator = JwtValidator.builder() + .addCriticalValidator() + .addHeaderFieldValidator(HEADER_CLAIM, "My test field", HEADER_CLAIM_VALUE) + .build(); + + Errors result = jwtValidator.validate(jwt); + result.checkValid(); + } + + @Test + public void testCriticalRequiredHeaderMissing() { + Jwt jwt = Jwt.builder() + .headerBuilder(builder -> builder.addHeaderCritical(HEADER_CLAIM)) + .build(); + + JwtValidator jwtValidator = JwtValidator.builder() + .addCriticalValidator() + .build(); + + Errors result = jwtValidator.validate(jwt); + assertThat(result.size(), is(1)); + assertThat(result.getFirst().getMessage(), is("JWT must contain [myTest], yet it contains: [crit]")); + } + + @Test + public void testCriticalDuplicate() { + Jwt jwt = Jwt.builder() + .headerBuilder(builder -> builder.addHeaderCritical(HEADER_CLAIM) + .addHeaderCritical(HEADER_CLAIM) + .addHeaderClaim(HEADER_CLAIM, HEADER_CLAIM_VALUE)) + .build(); + + JwtValidator jwtValidator = JwtValidator.builder() + .addCriticalValidator() + .build(); + + Errors result = jwtValidator.validate(jwt); + assertThat(result.size(), is(1)); + assertThat(result.getFirst().getMessage(), is("JWT critical header contains duplicated values: [myTest, myTest]")); + } + + @Test + public void testCriticalHeaderNotProcessed() { + Jwt jwt = Jwt.builder() + .headerBuilder(builder -> builder.addHeaderCritical(HEADER_CLAIM) + .addHeaderClaim(HEADER_CLAIM, HEADER_CLAIM_VALUE)) + .build(); + + JwtValidator jwtValidator = JwtValidator.builder() + .addCriticalValidator() + .build(); + + Errors result = jwtValidator.validate(jwt); + assertThat(result.size(), is(1)); + assertThat(result.getFirst().getMessage(), is("JWT is required to process [myTest], yet it process only [crit]")); + } + + @Test + public void testCriticalReservedRfcHeader() { + Jwt jwt = Jwt.builder() + .headerBuilder(builder -> builder.addHeaderCritical(JwtHeaders.ALGORITHM) + .addHeaderClaim(JwtHeaders.ALGORITHM, "123")) + .build(); + + JwtValidator jwtValidator = JwtValidator.builder() + .addCriticalValidator() + .build(); + + Errors result = jwtValidator.validate(jwt); + assertThat(result.size(), is(1)); + assertThat(result.getFirst().getMessage(), is("Required critical header value 'alg' is invalid. " + + "This required header is defined among JWA, JWE or JWS headers.")); + } + +} diff --git a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderBase.java b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderBase.java index 66d90e57841..ecc0a3b63df 100644 --- a/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderBase.java +++ b/security/providers/idcs-mapper/src/main/java/io/helidon/security/providers/idcs/mapper/IdcsRoleMapperProviderBase.java @@ -44,8 +44,8 @@ import io.helidon.security.SubjectType; import io.helidon.security.integration.common.RoleMapTracing; import io.helidon.security.jwt.Jwt; +import io.helidon.security.jwt.JwtValidator; import io.helidon.security.jwt.SignedJwt; -import io.helidon.security.jwt.Validator; import io.helidon.security.providers.oidc.common.OidcConfig; import io.helidon.security.spi.SubjectMappingProvider; import io.helidon.webclient.api.HttpClientRequest; @@ -382,7 +382,9 @@ public B addSubjectType(SubjectType type) { * Reactive token for app access to IDCS. */ protected static class AppToken { - private static final List> TIME_VALIDATORS = Jwt.defaultTimeValidators(); + private static final JwtValidator TIME_VALIDATORS = JwtValidator.builder() + .addDefaultTimeValidators() + .build(); private final AtomicReference> token = new AtomicReference<>(); private final WebClient webClient; @@ -411,7 +413,7 @@ protected Optional getToken(RoleMapTracing tracing) { AppTokenData tokenData = currentTokenData.get(); Jwt jwt = tokenData.appJwt(); if (jwt == null - || !jwt.validate(TIME_VALIDATORS).isValid() + || !TIME_VALIDATORS.validate(jwt).isValid() || isNearExpiration(jwt)) { // it is not valid or is very close to expiration - we must get a new value LazyValue newLazyValue = LazyValue.create(() -> fromServer(tracing)); diff --git a/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProvider.java b/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProvider.java index aff9c7d1fba..1ee7a45fa90 100644 --- a/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProvider.java +++ b/security/providers/jwt/src/main/java/io/helidon/security/providers/jwt/JwtProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 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. @@ -44,6 +44,7 @@ import io.helidon.security.jwt.Jwt; import io.helidon.security.jwt.JwtException; import io.helidon.security.jwt.JwtUtil; +import io.helidon.security.jwt.JwtValidator; import io.helidon.security.jwt.SignedJwt; import io.helidon.security.jwt.jwk.Jwk; import io.helidon.security.jwt.jwk.JwkKeys; @@ -167,7 +168,13 @@ private AuthenticationResponse authenticateToken(String token) { if (errors.isValid()) { Jwt jwt = signedJwt.getJwt(); // perform all validations, including expected audience verification - Errors validate = jwt.validate(null, expectedAudience); + JwtValidator jwtValidator = JwtValidator.builder() + .addDefaultTimeValidators() + .addCriticalValidator() + .addUserPrincipalValidator() + .addAudienceValidator(expectedAudience) + .build(); + Errors validate = jwtValidator.validate(jwt); if (validate.isValid()) { return AuthenticationResponse.success(buildSubject(jwt, signedJwt)); } else { diff --git a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java index 70dc6c23285..5fc5b12a291 100644 --- a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java +++ b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java @@ -60,6 +60,7 @@ import io.helidon.security.jwt.Jwt; import io.helidon.security.jwt.JwtException; import io.helidon.security.jwt.JwtUtil; +import io.helidon.security.jwt.JwtValidator; import io.helidon.security.jwt.SignedJwt; import io.helidon.security.jwt.jwk.JwkKeys; import io.helidon.security.providers.common.TokenCredential; @@ -85,6 +86,9 @@ class TenantAuthenticationHandler { private static final TokenHandler PARAM_HEADER_HANDLER = TokenHandler.forHeader(OidcConfig.PARAM_HEADER_NAME); private static final TokenHandler PARAM_ID_HEADER_HANDLER = TokenHandler.forHeader(OidcConfig.PARAM_ID_HEADER_NAME); private static final LazyValue RANDOM = LazyValue.create(SecureRandom::new); + private static final JwtValidator TIME_VALIDATORS = JwtValidator.builder() + .addDefaultTimeValidators() + .build(); private final boolean optional; private final OidcConfig oidcConfig; @@ -522,9 +526,19 @@ private AuthenticationResponse validateIdToken(String tenantId, ProviderRequest errors = Errors.collector().collect(); } Jwt jwt = signedJwt.getJwt(); - Errors validationErrors = jwt.validate(tenant.issuer(), - tenantConfig.clientId(), - true); + + JwtValidator.Builder jwtValidatorBuilder = JwtValidator.builder() + .addDefaultTimeValidators() + .addCriticalValidator() + .addUserPrincipalValidator() + .addAudienceValidator(tenantConfig.clientId()); + + if (tenant.issuer() != null) { + jwtValidatorBuilder.addIssuerValidator(tenant.issuer()); + } + + JwtValidator jwtValidation = jwtValidatorBuilder.build(); + Errors validationErrors = jwtValidation.validate(jwt); if (errors.isValid() && validationErrors.isValid()) { return processAccessToken(tenantId, providerRequest, jwt); @@ -569,7 +583,7 @@ private AuthenticationResponse validateAccessToken(String tenantId, } else { collector = Errors.collector(); } - Errors timeErrors = signedJwt.getJwt().validate(Jwt.defaultTimeValidators()); + Errors timeErrors = TIME_VALIDATORS.validate(signedJwt.getJwt()); if (timeErrors.isValid()) { return processValidationResult(providerRequest, signedJwt, idToken, tenantId, collector); } else { @@ -703,9 +717,18 @@ private AuthenticationResponse processValidationResult(ProviderRequest providerR List cookies) { Jwt jwt = signedJwt.getJwt(); Errors errors = collector.collect(); - Errors validationErrors = jwt.validate(tenant.issuer(), - tenantConfig.audience(), - tenantConfig.checkAudience()); + JwtValidator.Builder jwtValidatorBuilder = JwtValidator.builder() + .addDefaultTimeValidators() + .addCriticalValidator() + .addUserPrincipalValidator(); + if (tenant.issuer() != null) { + jwtValidatorBuilder.addIssuerValidator(tenant.issuer()); + } + if (tenantConfig.checkAudience()) { + jwtValidatorBuilder.addAudienceValidator(tenantConfig.audience()); + } + JwtValidator jwtValidation = jwtValidatorBuilder.build(); + Errors validationErrors = jwtValidation.validate(jwt); if (errors.isValid() && validationErrors.isValid()) { From dc26d362d25df8a0212c9c9bcc2e1d732010c947 Mon Sep 17 00:00:00 2001 From: Andrei Arlou Date: Thu, 13 Jun 2024 20:11:44 +0300 Subject: [PATCH 051/245] 4.x: Replace deprecated method Header.value() on Header.get() (#8873) --- .../testing/http/junit5/HttpHeaderMatcher.java | 4 ++-- .../security/basicauth/BasicExampleTest.java | 2 +- .../examples/webserver/echo/EchoClient.java | 4 ++-- .../encoding/ContentEncodingSupportImpl.java | 4 ++-- .../helidon/http/ClientResponseHeadersImpl.java | 4 ++-- .../io/helidon/http/ServerRequestHeaders.java | 10 +++++----- .../helidon/http/ServerRequestHeadersImpl.java | 2 +- .../io/helidon/http/ContentDispositionTest.java | 4 ++-- .../java/io/helidon/http/http2/Http2Headers.java | 6 +++--- .../io/helidon/http/http2/Http2HeadersTest.java | 14 +++++++------- .../media/multipart/ReadablePartAbstract.java | 4 ++-- .../integrations/common/rest/RestApiTest.java | 4 ++-- .../webclient/http1/Http1CallChainBase.java | 2 +- .../http1/Http1CallOutputStreamChain.java | 8 ++++---- .../webclient/http1/RedirectionProcessor.java | 2 +- .../webclient/websocket/WsClientImpl.java | 4 ++-- .../helidon/webserver/cors/AbstractCorsTest.java | 16 ++++++++-------- .../webserver/http1/Http1ServerRequest.java | 2 +- .../helidon/webserver/websocket/WsUpgrader.java | 8 ++++---- 19 files changed, 52 insertions(+), 52 deletions(-) diff --git a/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/HttpHeaderMatcher.java b/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/HttpHeaderMatcher.java index 49eb2b41651..a75c30f41c3 100644 --- a/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/HttpHeaderMatcher.java +++ b/common/testing/http-junit5/src/main/java/io/helidon/common/testing/http/junit5/HttpHeaderMatcher.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. @@ -217,7 +217,7 @@ protected boolean matchesSafely(Headers httpHeaders) { if (httpHeaders.contains(name)) { Header headerValue = httpHeaders.get(name); if (headerValue.allValues().size() == 1) { - return valuesMatcher.matches(headerValue.value()); + return valuesMatcher.matches(headerValue.get()); } return false; } diff --git a/examples/security/basic-auth-with-static-content/src/test/java/io/helidon/examples/security/basicauth/BasicExampleTest.java b/examples/security/basic-auth-with-static-content/src/test/java/io/helidon/examples/security/basicauth/BasicExampleTest.java index 34874e6ab2c..37a9a72414d 100644 --- a/examples/security/basic-auth-with-static-content/src/test/java/io/helidon/examples/security/basicauth/BasicExampleTest.java +++ b/examples/security/basic-auth-with-static-content/src/test/java/io/helidon/examples/security/basicauth/BasicExampleTest.java @@ -132,7 +132,7 @@ private void testNotAuthorized(String uri) { try (Http1ClientResponse response = client.get().uri(uri).request()) { assertThat(response.status(), is(Status.UNAUTHORIZED_401)); - String header = response.headers().get(HeaderNames.WWW_AUTHENTICATE).value(); + String header = response.headers().get(HeaderNames.WWW_AUTHENTICATE).get(); assertThat(header.toLowerCase(), containsString("basic")); assertThat(header, containsString("helidon")); diff --git a/examples/webserver/echo/src/main/java/io/helidon/examples/webserver/echo/EchoClient.java b/examples/webserver/echo/src/main/java/io/helidon/examples/webserver/echo/EchoClient.java index 933fa093bfb..0460ff49fe3 100644 --- a/examples/webserver/echo/src/main/java/io/helidon/examples/webserver/echo/EchoClient.java +++ b/examples/webserver/echo/src/main/java/io/helidon/examples/webserver/echo/EchoClient.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. @@ -53,7 +53,7 @@ public static void main(String[] args) { Headers headers = response.headers(); for (Header header : headers) { - System.out.println("Header: " + header.name() + "=" + header.value()); + System.out.println("Header: " + header.name() + "=" + header.get()); } System.out.println("Entity:"); System.out.println(response.as(String.class)); diff --git a/http/encoding/encoding/src/main/java/io/helidon/http/encoding/ContentEncodingSupportImpl.java b/http/encoding/encoding/src/main/java/io/helidon/http/encoding/ContentEncodingSupportImpl.java index 2b52b6da4d2..0215f447d3b 100644 --- a/http/encoding/encoding/src/main/java/io/helidon/http/encoding/ContentEncodingSupportImpl.java +++ b/http/encoding/encoding/src/main/java/io/helidon/http/encoding/ContentEncodingSupportImpl.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. @@ -120,7 +120,7 @@ public ContentEncoder encoder(Headers headers) { return ContentEncoder.NO_OP; } - String acceptEncoding = headers.get(HeaderNames.ACCEPT_ENCODING).value(); + String acceptEncoding = headers.get(HeaderNames.ACCEPT_ENCODING).get(); /* Accept-Encoding: gzip Accept-Encoding: gzip, compress, br diff --git a/http/http/src/main/java/io/helidon/http/ClientResponseHeadersImpl.java b/http/http/src/main/java/io/helidon/http/ClientResponseHeadersImpl.java index 3b90ec2969a..1ed604d5185 100644 --- a/http/http/src/main/java/io/helidon/http/ClientResponseHeadersImpl.java +++ b/http/http/src/main/java/io/helidon/http/ClientResponseHeadersImpl.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. @@ -56,7 +56,7 @@ public Header get(HeaderName name) { public Optional contentType() { if (parserMode == ParserMode.RELAXED) { return contains(HeaderNameEnum.CONTENT_TYPE) - ? Optional.of(HttpMediaType.create(get(HeaderNameEnum.CONTENT_TYPE).value(), parserMode)) + ? Optional.of(HttpMediaType.create(get(HeaderNameEnum.CONTENT_TYPE).get(), parserMode)) : Optional.empty(); } return headers.contentType(); diff --git a/http/http/src/main/java/io/helidon/http/ServerRequestHeaders.java b/http/http/src/main/java/io/helidon/http/ServerRequestHeaders.java index 38cd32c9b9a..528b21e1af0 100644 --- a/http/http/src/main/java/io/helidon/http/ServerRequestHeaders.java +++ b/http/http/src/main/java/io/helidon/http/ServerRequestHeaders.java @@ -78,7 +78,7 @@ static ServerRequestHeaders create() { default Optional ifModifiedSince() { if (contains(HeaderNames.IF_MODIFIED_SINCE)) { return Optional.of(get(HeaderNames.IF_MODIFIED_SINCE)) - .map(Header::value) + .map(Header::get) .map(DateTime::parse); } @@ -95,7 +95,7 @@ default Optional ifModifiedSince() { default Optional ifUnmodifiedSince() { if (contains(HeaderNames.IF_UNMODIFIED_SINCE)) { return Optional.of(get(HeaderNames.IF_UNMODIFIED_SINCE)) - .map(Header::value) + .map(Header::get) .map(DateTime::parse); } return Optional.empty(); @@ -189,7 +189,7 @@ default Parameters cookies() { default Optional acceptDatetime() { if (contains(HeaderNames.ACCEPT_DATETIME)) { return Optional.of(get(HeaderNames.ACCEPT_DATETIME)) - .map(Header::value) + .map(Header::get) .map(DateTime::parse); } return Optional.empty(); @@ -203,7 +203,7 @@ default Optional acceptDatetime() { default Optional date() { if (contains(HeaderNames.DATE)) { return Optional.of(get(HeaderNames.DATE)) - .map(Header::value) + .map(Header::get) .map(DateTime::parse); } return Optional.empty(); @@ -221,7 +221,7 @@ default Optional date() { default Optional referer() { if (contains(HeaderNames.REFERER)) { return Optional.of(get(HeaderNames.REFERER)) - .map(Header::value) + .map(Header::get) .map(URI::create); } return Optional.empty(); diff --git a/http/http/src/main/java/io/helidon/http/ServerRequestHeadersImpl.java b/http/http/src/main/java/io/helidon/http/ServerRequestHeadersImpl.java index 20b5d066645..ef48b992bca 100644 --- a/http/http/src/main/java/io/helidon/http/ServerRequestHeadersImpl.java +++ b/http/http/src/main/java/io/helidon/http/ServerRequestHeadersImpl.java @@ -77,7 +77,7 @@ public List acceptedTypes() { List acceptedTypes; List acceptValues = all(HeaderNames.ACCEPT, List::of); - if (acceptValues.size() == 1 && HUC_ACCEPT_DEFAULT.value().equals(acceptValues.get(0))) { + if (acceptValues.size() == 1 && HUC_ACCEPT_DEFAULT.get().equals(acceptValues.get(0))) { acceptedTypes = HUC_ACCEPT_DEFAULT_TYPES; } else { acceptedTypes = new ArrayList<>(5); diff --git a/http/http/src/test/java/io/helidon/http/ContentDispositionTest.java b/http/http/src/test/java/io/helidon/http/ContentDispositionTest.java index bef61a70b63..3a012cb3290 100644 --- a/http/http/src/test/java/io/helidon/http/ContentDispositionTest.java +++ b/http/http/src/test/java/io/helidon/http/ContentDispositionTest.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. @@ -224,7 +224,7 @@ void testQuotes() { .filename("file.txt") .size(300) .build(); - assertThat(cd.value(), is(equalTo(template))); + assertThat(cd.get(), is(equalTo(template))); } @Test diff --git a/http/http2/src/main/java/io/helidon/http/http2/Http2Headers.java b/http/http2/src/main/java/io/helidon/http/http2/Http2Headers.java index 62c9742a993..87c9537fc56 100644 --- a/http/http2/src/main/java/io/helidon/http/http2/Http2Headers.java +++ b/http/http2/src/main/java/io/helidon/http/http2/Http2Headers.java @@ -453,7 +453,7 @@ public void write(DynamicTable table, Http2HuffmanEncoder huffman, BufferData gr } for (Header header : headers) { - String value = header.value(); + String value = header.get(); boolean shouldIndex = !header.changing(); boolean neverIndex = header.sensitive(); @@ -649,7 +649,7 @@ private static Http2Headers createFromWritable(WritableHeaders headers) { headers.remove(HeaderNames.HOST, it -> { if (!pseudoHeaders.hasAuthority()) { - pseudoHeaders.authority(it.value()); + pseudoHeaders.authority(it.get()); } }); @@ -689,7 +689,7 @@ the stream (see Section 5.3). Add one to the value to obtain a private static void removeFromHeadersAddToPseudo(WritableHeaders headers, Consumer valueConsumer, HeaderName pseudoHeader) { - headers.remove(pseudoHeader, it -> valueConsumer.accept(it.value())); + headers.remove(pseudoHeader, it -> valueConsumer.accept(it.get())); } private void writeHeader(Http2HuffmanEncoder huffman, DynamicTable table, diff --git a/http/http2/src/test/java/io/helidon/http/http2/Http2HeadersTest.java b/http/http2/src/test/java/io/helidon/http/http2/Http2HeadersTest.java index 1aa703f9337..f27d7d77eb5 100644 --- a/http/http2/src/test/java/io/helidon/http/http2/Http2HeadersTest.java +++ b/http/http2/src/test/java/io/helidon/http/http2/Http2HeadersTest.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. @@ -46,7 +46,7 @@ void testC_2_1() { DynamicTable dynamicTable = DynamicTable.create(Http2Settings.create()); Headers requestHeaders = headers(hexEncoded, dynamicTable).httpHeaders(); - assertThat(requestHeaders.get(HeaderNames.create("custom-key")).value(), is("custom-header")); + assertThat(requestHeaders.get(HeaderNames.create("custom-key")).get(), is("custom-header")); } BufferData data(String hexEncoded) { @@ -76,7 +76,7 @@ void testC_2_3() { DynamicTable dynamicTable = DynamicTable.create(Http2Settings.create()); Headers requestHeaders = headers(hexEncoded, dynamicTable).httpHeaders(); - assertThat(requestHeaders.get(HeaderNames.create("password")).value(), is("secret")); + assertThat(requestHeaders.get(HeaderNames.create("password")).get(), is("secret")); assertThat("Dynamic table should be empty", dynamicTable.currentTableSize(), is(0)); } @@ -124,7 +124,7 @@ void testC_3() { assertThat(http2Headers.scheme(), is("http")); assertThat(http2Headers.path(), is("/")); assertThat(http2Headers.authority(), is("www.example.com")); - assertThat(requestHeaders.get(HeaderNames.create("cache-control")).value(), is("no-cache")); + assertThat(requestHeaders.get(HeaderNames.create("cache-control")).get(), is("no-cache")); assertThat("Dynamic table should not be empty", dynamicTable.currentTableSize(), not(0)); @@ -145,7 +145,7 @@ void testC_3() { assertThat(http2Headers.scheme(), is("https")); assertThat(http2Headers.path(), is("/index.html")); assertThat(http2Headers.authority(), is("www.example.com")); - assertThat(requestHeaders.get(CUSTOM_HEADER_NAME).value(), is("custom-value")); + assertThat(requestHeaders.get(CUSTOM_HEADER_NAME).get(), is("custom-value")); assertThat("Dynamic table should not be empty", dynamicTable.currentTableSize(), not(0)); @@ -214,7 +214,7 @@ void testC_4() { assertThat(http2Headers.scheme(), is("http")); assertThat(http2Headers.path(), is("/")); assertThat(http2Headers.authority(), is("www.example.com")); - assertThat(requestHeaders.get(HeaderNames.create("cache-control")).value(), is("no-cache")); + assertThat(requestHeaders.get(HeaderNames.create("cache-control")).get(), is("no-cache")); assertThat("Dynamic table should not be empty", dynamicTable.currentTableSize(), not(0)); @@ -235,7 +235,7 @@ void testC_4() { assertThat(http2Headers.scheme(), is("https")); assertThat(http2Headers.path(), is("/index.html")); assertThat(http2Headers.authority(), is("www.example.com")); - assertThat(requestHeaders.get(CUSTOM_HEADER_NAME).value(), is("custom-value")); + assertThat(requestHeaders.get(CUSTOM_HEADER_NAME).get(), is("custom-value")); assertThat("Dynamic table should not be empty", dynamicTable.currentTableSize(), not(0)); diff --git a/http/media/multipart/src/main/java/io/helidon/http/media/multipart/ReadablePartAbstract.java b/http/media/multipart/src/main/java/io/helidon/http/media/multipart/ReadablePartAbstract.java index f1e6a1bcc27..11d53a8a469 100644 --- a/http/media/multipart/src/main/java/io/helidon/http/media/multipart/ReadablePartAbstract.java +++ b/http/media/multipart/src/main/java/io/helidon/http/media/multipart/ReadablePartAbstract.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. @@ -80,7 +80,7 @@ public boolean hasEntity() { private void contentDisposition() { if (headers.contains(HeaderNames.CONTENT_DISPOSITION)) { - this.contentDisposition = ContentDisposition.parse(headers.get(HeaderNames.CONTENT_DISPOSITION).value()); + this.contentDisposition = ContentDisposition.parse(headers.get(HeaderNames.CONTENT_DISPOSITION).get()); } else { this.contentDisposition = ContentDisposition.empty(); } diff --git a/integrations/common/rest/src/test/java/io/helidon/integrations/common/rest/RestApiTest.java b/integrations/common/rest/src/test/java/io/helidon/integrations/common/rest/RestApiTest.java index 145812c4fff..32165b38c9d 100644 --- a/integrations/common/rest/src/test/java/io/helidon/integrations/common/rest/RestApiTest.java +++ b/integrations/common/rest/src/test/java/io/helidon/integrations/common/rest/RestApiTest.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. @@ -158,7 +158,7 @@ private JsonObjectBuilder common(ServerRequest req) { ServerRequestHeaders headers = req.headers(); if (headers.size() > 0) { JsonObjectBuilder headersBuilder = JSON.createObjectBuilder(); - headers.forEach(header -> headersBuilder.add(header.name(), header.value())); + headers.forEach(header -> headersBuilder.add(header.name(), header.get())); objectBuilder.add("headers", headersBuilder); } diff --git a/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallChainBase.java b/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallChainBase.java index bd7e0db61cc..efc20d457d2 100644 --- a/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallChainBase.java +++ b/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallChainBase.java @@ -250,7 +250,7 @@ private static InputStream inputStream(HttpClientConfig clientConfig, ContentDecoder decoder; if (encodingSupport.contentDecodingEnabled() && responseHeaders.contains(HeaderNames.CONTENT_ENCODING)) { - String contentEncoding = responseHeaders.get(HeaderNames.CONTENT_ENCODING).value(); + String contentEncoding = responseHeaders.get(HeaderNames.CONTENT_ENCODING).get(); if (encodingSupport.contentDecodingSupported(contentEncoding)) { decoder = encodingSupport.decoder(contentEncoding); } else { diff --git a/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallOutputStreamChain.java b/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallOutputStreamChain.java index 78fb1206a7f..1532f9d4777 100644 --- a/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallOutputStreamChain.java +++ b/webclient/http1/src/main/java/io/helidon/webclient/http1/Http1CallOutputStreamChain.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. @@ -130,7 +130,7 @@ WebClientServiceResponse doProceed(ClientConnection connection, if (originalRequest().followRedirects() && RedirectionProcessor.redirectionStatusCode(responseStatus)) { checkRedirectHeaders(responseHeaders); - URI newUri = URI.create(responseHeaders.get(HeaderNames.LOCATION).value()); + URI newUri = URI.create(responseHeaders.get(HeaderNames.LOCATION).get()); ClientUri redirectUri = ClientUri.create(newUri); if (newUri.getHost() == null) { UriInfo resolvedUri = cos.lastRequest.resolvedUri(); @@ -440,7 +440,7 @@ private void sendPrologueAndHeader() { } private void redirect(Status lastStatus, WritableHeaders headerValues) { - String redirectedUri = headerValues.get(HeaderNames.LOCATION).value(); + String redirectedUri = headerValues.get(HeaderNames.LOCATION).get(); ClientUri lastUri = originalRequest.uri(); Method method; boolean sendEntity; @@ -492,7 +492,7 @@ private void redirect(Status lastStatus, WritableHeaders headerValues) { method = Method.GET; sendEntity = false; } - redirectedUri = response.headers().get(HeaderNames.LOCATION).value(); + redirectedUri = response.headers().get(HeaderNames.LOCATION).get(); } } else { if (!sendEntity) { diff --git a/webclient/http1/src/main/java/io/helidon/webclient/http1/RedirectionProcessor.java b/webclient/http1/src/main/java/io/helidon/webclient/http1/RedirectionProcessor.java index 0ea7b39d733..4ae672c6dcc 100644 --- a/webclient/http1/src/main/java/io/helidon/webclient/http1/RedirectionProcessor.java +++ b/webclient/http1/src/main/java/io/helidon/webclient/http1/RedirectionProcessor.java @@ -56,7 +56,7 @@ static Http1ClientResponseImpl invokeWithFollowRedirects(Http1ClientRequestImpl + " header present in the response! " + "It is not clear where to redirect."); } - String redirectedUri = clientResponse.headers().get(HeaderNames.LOCATION).value(); + String redirectedUri = clientResponse.headers().get(HeaderNames.LOCATION).get(); URI newUri = URI.create(redirectedUri); ClientUri redirectUri = ClientUri.create(newUri); diff --git a/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClientImpl.java b/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClientImpl.java index 63c3a58615c..ee09ea95835 100644 --- a/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClientImpl.java +++ b/webclient/websocket/src/main/java/io/helidon/webclient/websocket/WsClientImpl.java @@ -142,14 +142,14 @@ public void connect(URI uri, WsListener listener) { + responseHeaders); } ClientConnection connection = upgradeResponse.connection(); - String secWsAccept = responseHeaders.get(HEADER_WS_ACCEPT).value(); + String secWsAccept = responseHeaders.get(HEADER_WS_ACCEPT).get(); if (!hash(connection.helidonSocket(), secWsKey).equals(secWsAccept)) { throw new WsClientException("Failed to upgrade to WebSocket, expected valid secWsKey. Headers: " + responseHeaders); } // we are upgraded, let's switch to web socket if (headers.contains(HEADER_WS_PROTOCOL)) { - session = new ClientWsConnection(connection, listener, headers.get(HEADER_WS_PROTOCOL).value()); + session = new ClientWsConnection(connection, listener, headers.get(HEADER_WS_PROTOCOL).get()); } else { session = new ClientWsConnection(connection, listener); } diff --git a/webserver/cors/src/test/java/io/helidon/webserver/cors/AbstractCorsTest.java b/webserver/cors/src/test/java/io/helidon/webserver/cors/AbstractCorsTest.java index d484aca0fc4..b2d3e146d35 100644 --- a/webserver/cors/src/test/java/io/helidon/webserver/cors/AbstractCorsTest.java +++ b/webserver/cors/src/test/java/io/helidon/webserver/cors/AbstractCorsTest.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. @@ -178,11 +178,11 @@ void test2PreFlightAllowedHeaders1() { assertThat(response.status(), is(Status.OK_200)); assertThat(response.headers() - .get(ACCESS_CONTROL_ALLOW_ORIGIN).value(), is(fooOrigin())); + .get(ACCESS_CONTROL_ALLOW_ORIGIN).get(), is(fooOrigin())); assertThat(response.headers() - .get(ACCESS_CONTROL_ALLOW_CREDENTIALS).value(), is("true")); + .get(ACCESS_CONTROL_ALLOW_CREDENTIALS).get(), is("true")); assertThat(response.headers() - .get(ACCESS_CONTROL_ALLOW_METHODS).value(), is("PUT")); + .get(ACCESS_CONTROL_ALLOW_METHODS).get(), is("PUT")); assertThat(response.headers() .get(ACCESS_CONTROL_ALLOW_HEADERS).values(), containsString(fooHeader())); assertThat(response.headers(), noHeader(ACCESS_CONTROL_MAX_AGE)); @@ -204,8 +204,8 @@ void test2PreFlightAllowedHeaders2() { assertThat(response.headers(), hasHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "http://foo.bar")); assertThat(response.headers(), hasHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true")); assertThat(response.headers(), hasHeader(ACCESS_CONTROL_ALLOW_METHODS, "PUT")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).value(), containsString("X-foo")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).value(), containsString("X-bar")); + assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).get(), containsString("X-foo")); + assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).get(), containsString("X-bar")); assertThat(response.headers(), noHeader(ACCESS_CONTROL_MAX_AGE)); } } @@ -226,8 +226,8 @@ void test2PreFlightAllowedHeaders3() { assertThat(response.headers(), hasHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "http://foo.bar")); assertThat(response.headers(), hasHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true")); assertThat(response.headers(), hasHeader(ACCESS_CONTROL_ALLOW_METHODS, "PUT")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).value(), containsString("X-foo")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).value(), containsString("X-bar")); + assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).get(), containsString("X-foo")); + assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS).get(), containsString("X-bar")); assertThat(response.headers(), noHeader(ACCESS_CONTROL_MAX_AGE)); } } diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java index 84a7a1e5e9c..86a29e7a8de 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerRequest.java @@ -182,7 +182,7 @@ public PeerInfo localPeer() { @Override public String authority() { - return headers.get(HeaderNames.HOST).value(); + return headers.get(HeaderNames.HOST).get(); } @Override diff --git a/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsUpgrader.java b/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsUpgrader.java index 71f21ca49a0..997e29518f4 100644 --- a/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsUpgrader.java +++ b/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsUpgrader.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. @@ -124,7 +124,7 @@ public String supportedProtocol() { public ServerConnection upgrade(ConnectionContext ctx, HttpPrologue prologue, WritableHeaders headers) { String wsKey; if (headers.contains(WS_KEY)) { - wsKey = headers.get(WS_KEY).value(); + wsKey = headers.get(WS_KEY).get(); } else { // this header is required return null; @@ -132,7 +132,7 @@ public ServerConnection upgrade(ConnectionContext ctx, HttpPrologue prologue, Wr // protocol version String version; if (headers.contains(WS_VERSION)) { - version = headers.get(WS_VERSION).value(); + version = headers.get(WS_VERSION).get(); } else { version = SUPPORTED_VERSION; } @@ -156,7 +156,7 @@ public ServerConnection upgrade(ConnectionContext ctx, HttpPrologue prologue, Wr if (!anyOrigin()) { if (headers.contains(HeaderNames.ORIGIN)) { - String origin = headers.get(HeaderNames.ORIGIN).value(); + String origin = headers.get(HeaderNames.ORIGIN).get(); if (!origins().contains(origin)) { throw RequestException.builder() .message("Invalid Origin") From 1ded39ac76228f7434aaf38d4a0a67891bdbf5b4 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Thu, 13 Jun 2024 19:19:56 -0500 Subject: [PATCH 052/245] Correct the ordering of whenSent in doc snippet (#8884) Signed-off-by: Tim Quinn --- .../main/java/io/helidon/docs/se/guides/MetricsSnippets.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/java/io/helidon/docs/se/guides/MetricsSnippets.java b/docs/src/main/java/io/helidon/docs/se/guides/MetricsSnippets.java index c38046ee399..c460c3229b5 100644 --- a/docs/src/main/java/io/helidon/docs/se/guides/MetricsSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/guides/MetricsSnippets.java @@ -174,8 +174,8 @@ public void routing(HttpRules rules) { private void getDefaultMessageHandler(ServerRequest request, ServerResponse response) { Timer.Sample timerSample = Timer.start(); // <3> - sendResponse(response, "Here are some cards ..."); response.whenSent(() -> timerSample.stop(cardTimer)); // <4> + sendResponse(response, "Here are some cards ..."); } private void sendResponse(ServerResponse response, String msg) { From ed37d4fee4ff5acee1f2109143b78391e5b83d64 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Mon, 17 Jun 2024 21:00:57 +0200 Subject: [PATCH 053/245] Fix multi-value query string parsing (#8889) * Fix multi-value query string parsing * Fixed handling of query methods. Reverted to use raw query (as intended), and fixed javadoc of the method. Added tests. --- .../helidon/common/uri/UriQueryWriteable.java | 9 ++++++--- .../common/uri/UriQueryWriteableImpl.java | 14 ++++++++++++-- .../io/helidon/common/uri/UriQueryTest.java | 17 +++++++++++++++-- .../io/helidon/webclient/api/ClientUri.java | 6 ++---- .../io/helidon/webclient/api/ClientUriTest.java | 16 +++++++++++++++- 5 files changed, 50 insertions(+), 12 deletions(-) diff --git a/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteable.java b/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteable.java index c8318896314..12310b8749f 100644 --- a/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteable.java +++ b/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteable.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. @@ -85,8 +85,11 @@ static UriQueryWriteable create() { UriQueryWriteable remove(String name, Consumer> removedConsumer); /** - * Update from a query string (with decoded values). - * @param queryString decoded query string to update this instance + * Update from a query string (with encoded values). + *

    + * This documentation (and behavior) has been changed, as we cannot create a proper query from {@code decoded} values, + * as these may contain characters used to split the query. + * @param queryString encoded query string to update this instance */ void fromQueryString(String queryString); diff --git a/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteableImpl.java b/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteableImpl.java index 2dc04a51670..b6c2ba45aa2 100644 --- a/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteableImpl.java +++ b/common/uri/src/main/java/io/helidon/common/uri/UriQueryWriteableImpl.java @@ -292,11 +292,21 @@ public String toString() { private void addRaw(String next) { int eq = next.indexOf('='); if (eq == -1) { - set(next); + addRaw(next, ""); } else { String name = next.substring(0, eq); String value = next.substring(eq + 1); - set(name, value); + addRaw(name, value); } } + + private void addRaw(String encodedName, String encodedValue) { + String decodedName = UriEncoding.decodeUri(encodedName); + String decodedValue = UriEncoding.decodeUri(encodedValue); + + rawQueryParams.computeIfAbsent(encodedName, it -> new ArrayList<>(1)) + .add(encodedValue); + decodedQueryParams.computeIfAbsent(decodedName, it -> new ArrayList<>(1)) + .add(decodedValue); + } } diff --git a/common/uri/src/test/java/io/helidon/common/uri/UriQueryTest.java b/common/uri/src/test/java/io/helidon/common/uri/UriQueryTest.java index edb01dd62bd..d9dc16fed24 100644 --- a/common/uri/src/test/java/io/helidon/common/uri/UriQueryTest.java +++ b/common/uri/src/test/java/io/helidon/common/uri/UriQueryTest.java @@ -57,7 +57,7 @@ void testEncodedOtherChars() { assertThat(uriQuery.get("h"), is("xc#e<")); assertThat(uriQuery.get("a"), is("b&c=d")); } - + @Test void testEmptyQueryString() { UriQuery uriQuery = UriQuery.create(""); @@ -80,10 +80,23 @@ void issue8710() { UriQuery uriQuery = UriQuery.create(URI.create("http://foo/bar?a&b=c")); OptionalValue optional = uriQuery.first("a"); assertThat(optional.isEmpty(), is(true)); - + assertThat(uriQuery.all("a"), hasItems()); assertThat(uriQuery.all("b"), hasItems("c")); assertThat(uriQuery.getRaw("a"), is("")); } + @Test + void testFromQueryString() { + UriQueryWriteable query = UriQueryWriteable.create(); + query.fromQueryString("p1=v1&p2=v2&p3=%2F%2Fv3%2F%2F&p4=a%20b%20c"); + assertThat(query.get("p1"), is("v1")); + assertThat(query.get("p2"), is("v2")); + assertThat(query.get("p3"), is("//v3//")); + // make sure the encoded value is correct + assertThat(query.getRaw("p3"), is("%2F%2Fv3%2F%2F")); + assertThat(query.get("p4"), is("a b c")); + assertThat(query.getRaw("p4"), is("a%20b%20c")); + } + } \ No newline at end of file diff --git a/webclient/api/src/main/java/io/helidon/webclient/api/ClientUri.java b/webclient/api/src/main/java/io/helidon/webclient/api/ClientUri.java index 9903579584a..c0f5403f52d 100644 --- a/webclient/api/src/main/java/io/helidon/webclient/api/ClientUri.java +++ b/webclient/api/src/main/java/io/helidon/webclient/api/ClientUri.java @@ -18,7 +18,6 @@ import java.net.URI; -import io.helidon.common.uri.UriEncoding; import io.helidon.common.uri.UriFragment; import io.helidon.common.uri.UriInfo; import io.helidon.common.uri.UriPath; @@ -194,11 +193,10 @@ public ClientUri resolve(URI uri) { uriBuilder.path(resolvePath(uriBuilder.path().path(), uri.getPath())); - String queryString = uri.getQuery(); + String queryString = uri.getRawQuery(); if (queryString != null) { // class URI does not decode +'s, so we do it here - query.fromQueryString(queryString.indexOf('+') >= 0 ? UriEncoding.decodeUri(queryString) - : queryString); + query.fromQueryString(queryString.replaceAll("\\+", "%20")); } if (uri.getRawFragment() != null) { diff --git a/webclient/api/src/test/java/io/helidon/webclient/api/ClientUriTest.java b/webclient/api/src/test/java/io/helidon/webclient/api/ClientUriTest.java index 56c29a00d7b..a670bc45586 100644 --- a/webclient/api/src/test/java/io/helidon/webclient/api/ClientUriTest.java +++ b/webclient/api/src/test/java/io/helidon/webclient/api/ClientUriTest.java @@ -20,8 +20,10 @@ import io.helidon.common.uri.UriPath; import io.helidon.common.uri.UriQueryWriteable; + import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -63,7 +65,12 @@ void testNonDefaults() { void testQueryParams() { UriQueryWriteable query = UriQueryWriteable.create(); query.fromQueryString("p1=v1&p2=v2&p3=%2F%2Fv3%2F%2F"); - ClientUri helper = ClientUri.create(URI.create("http://localhost:8080/loom/quick?" + query.value())); + assertThat(query.get("p1"), is("v1")); + assertThat(query.get("p2"), is("v2")); + assertThat(query.get("p3"), is("//v3//")); + assertThat(query.getRaw("p3"), is("%2F%2Fv3%2F%2F")); + + ClientUri helper = ClientUri.create(URI.create("http://localhost:8080/loom/quick?" + query.rawValue())); assertThat(helper.authority(), is("localhost:8080")); assertThat(helper.host(), is("localhost")); @@ -76,6 +83,13 @@ void testQueryParams() { assertThat(helper.query().getRaw("p3"), is("%2F%2Fv3%2F%2F")); } + @Test + void testQueryMultipleValues() { + UriQueryWriteable query = UriQueryWriteable.create(); + query.fromQueryString("p1=v1&p1=v2"); + assertThat(query.all("p1"), hasItems("v1", "v2")); + } + @Test void testResolvePath() { ClientUri helper = ClientUri.create(URI.create("http://localhost:8080/loom")); From d94706db173727d8b575dce5ffa4dbcb377df4c5 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Mon, 17 Jun 2024 23:32:07 +0200 Subject: [PATCH 054/245] 4.x: Fixed configuration metadata of blueprints that are configured and provide a service (#8891) * Fixed configuration metadata of protocol configs. * Updated generated documentation to latest state of code. * Fixed additional providers blueprints to add config key. * Added validation of @Provides with @Configured - it must provide a configuration key now. * Validation errors now contain source type for better error messages. --- .../io/helidon/builder/codegen/Types.java | 1 + .../builder/codegen/ValidationTask.java | 26 +++++++++++++++---- .../io/helidon/builder/codegen/TypesTest.java | 1 + .../io_helidon_webserver_ListenerConfig.adoc | 11 +++++++- .../io_helidon_webserver_WebServer.adoc | 11 +++++++- .../io_helidon_webserver_cors_CorsConfig.adoc | 2 +- ...io_helidon_webserver_cors_CorsFeature.adoc | 2 +- .../io_helidon_webserver_grpc_GrpcConfig.adoc | 10 +++++-- ...o_helidon_webserver_http1_Http1Config.adoc | 8 +++++- ...o_helidon_webserver_http2_Http2Config.adoc | 8 +++++- ...idon_webserver_observe_ObserveFeature.adoc | 8 +++--- ...bserver_observe_config_ConfigObserver.adoc | 6 +++++ ...n_webserver_observe_info_InfoObserver.adoc | 6 +++++ ...don_webserver_observe_log_LogObserver.adoc | 6 +++++ ...erver_observe_tracing_TracingObserver.adoc | 6 +++++ ..._helidon_webserver_websocket_WsConfig.adoc | 8 +++++- .../webserver/grpc/GrpcConfigBlueprint.java | 5 ++-- .../webserver/http2/Http2ConfigBlueprint.java | 5 ++-- .../config/ConfigObserverConfigBlueprint.java | 4 +-- .../info/InfoObserverConfigBlueprint.java | 4 +-- .../log/LogObserverConfigBlueprint.java | 4 +-- webserver/observe/tracing/pom.xml | 5 ++++ .../TracingObserverConfigBlueprint.java | 5 ++-- .../webserver/http1/Http1ConfigBlueprint.java | 5 ++-- .../websocket/WsConfigBlueprint.java | 5 ++-- 25 files changed, 128 insertions(+), 34 deletions(-) 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 8a274abb2cb..3fb26ec7436 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 @@ -43,6 +43,7 @@ final class Types { static final TypeName PROTOTYPE_ANNOTATED = TypeName.create("io.helidon.builder.api.Prototype.Annotated"); static final TypeName PROTOTYPE_FACTORY = TypeName.create("io.helidon.builder.api.Prototype.Factory"); static final TypeName PROTOTYPE_CONFIGURED = TypeName.create("io.helidon.builder.api.Prototype.Configured"); + static final TypeName PROTOTYPE_PROVIDES = TypeName.create("io.helidon.builder.api.Prototype.Provides"); static final TypeName PROTOTYPE_BUILDER = TypeName.create("io.helidon.builder.api.Prototype.Builder"); static final TypeName PROTOTYPE_CONFIGURED_BUILDER = TypeName.create("io.helidon.builder.api.Prototype.ConfiguredBuilder"); static final TypeName PROTOTYPE_CUSTOM_METHODS = TypeName.create("io.helidon.builder.api.Prototype.CustomMethods"); diff --git a/builder/codegen/src/main/java/io/helidon/builder/codegen/ValidationTask.java b/builder/codegen/src/main/java/io/helidon/builder/codegen/ValidationTask.java index f0aa71335cb..f9ff370e4a1 100644 --- a/builder/codegen/src/main/java/io/helidon/builder/codegen/ValidationTask.java +++ b/builder/codegen/src/main/java/io/helidon/builder/codegen/ValidationTask.java @@ -23,6 +23,7 @@ import io.helidon.codegen.ElementInfoPredicates; import io.helidon.common.Errors; import io.helidon.common.types.AccessModifier; +import io.helidon.common.types.Annotation; import io.helidon.common.types.TypeInfo; import io.helidon.common.types.TypeName; import io.helidon.common.types.TypedElementInfo; @@ -41,7 +42,7 @@ private static void validateImplements(Errors.Collector errors, if (validatedType.interfaceTypeInfo() .stream() .noneMatch(it -> it.typeName().equals(implementedInterface))) { - errors.fatal(message); + errors.fatal(validatedType.typeName(), message); } } @@ -70,7 +71,7 @@ private static void validateFactoryMethod(Errors.Collector errors, }) .findFirst() .isEmpty()) { - errors.fatal(validatedType.typeName().fqName(), message); + errors.fatal(validatedType.typeName(), message); } } @@ -166,7 +167,21 @@ static class ValidateBlueprint extends ValidationTask { public void validate(Errors.Collector errors) { // must be package local if (blueprint.accessModifier() == AccessModifier.PUBLIC) { - errors.fatal(blueprint.typeName().fqName() + " is defined as public, it must be package local"); + errors.fatal(blueprint.typeName(), blueprint.typeName().fqName() + + " is defined as public, it must be package local"); + } + + // if configured & provides, must have config key + if (blueprint.hasAnnotation(Types.PROTOTYPE_CONFIGURED) + && blueprint.hasAnnotation(Types.PROTOTYPE_PROVIDES)) { + Annotation configured = blueprint.annotation(Types.PROTOTYPE_CONFIGURED); + String value = configured.stringValue().orElse(""); + if (value.isEmpty()) { + // we have a @Configured and @Provides - this should have a configuration key! + errors.fatal(blueprint.typeName(), blueprint.typeName().fqName() + + " is marked as @Configured and @Provides, yet it does not" + + " define a configuration key"); + } } } @@ -276,8 +291,9 @@ void validate(Errors.Collector errors) { if (typeInfo.findAnnotation(annotation) .stream() .noneMatch(it -> it.value().map(expectedValue::equals).orElse(false))) { - errors.fatal("Type " + typeInfo.typeName() - .fqName() + " must be annotated with " + annotation.fqName() + "(" + expectedValue + ")"); + errors.fatal(typeInfo.typeName(), + "Type " + typeInfo.typeName().fqName() + + " must be annotated with " + annotation.fqName() + "(" + expectedValue + ")"); } } } 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 index f5c1d35db9c..f0d2714df0c 100644 --- 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 @@ -94,6 +94,7 @@ void testTypes() { 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_PROVIDES", Prototype.Provides.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); diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc index 5ab27735bf5..a77b8f500ba 100644 --- a/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_ListenerConfig.adoc @@ -110,7 +110,16 @@ Type: link:{javadoc-base-url}/io.helidon.webserver/io/helidon/webserver/Listener If configured to `0` (the default), server starts on a random port. Port to listen on (for the default socket) -|`protocols` |io.helidon.webserver.spi.ProtocolConfig[] (service provider interface) |{nbsp} |Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1. +|`protocols` |io.helidon.webserver.spi.ProtocolConfig[] (service provider interface) + +Such as: + + - xref:{rootdir}/config/io_helidon_webserver_http2_Http2Config.adoc[http_2 (Http2Config)] + - xref:{rootdir}/config/io_helidon_webserver_grpc_GrpcConfig.adoc[grpc (GrpcConfig)] + - xref:{rootdir}/config/io_helidon_webserver_websocket_WsConfig.adoc[websocket (WsConfig)] + - xref:{rootdir}/config/io_helidon_webserver_http1_Http1Config.adoc[http_1_1 (Http1Config)] + + |{nbsp} |Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1. As the order is not important (providers are ordered by weight by default), we can use a configuration as an object, such as:

    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc
    index cd8f542191c..666d8727631 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_WebServer.adoc
    @@ -126,7 +126,16 @@ Such as:
      If configured to `0` (the default), server starts on a random port.
     
      Port to listen on (for the default socket)
    -|`protocols` |io.helidon.webserver.spi.ProtocolConfig[] (service provider interface) |{nbsp} |Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1.
    +|`protocols` |io.helidon.webserver.spi.ProtocolConfig[] (service provider interface)
    +
    +Such as:
    +
    + - xref:{rootdir}/config/io_helidon_webserver_http2_Http2Config.adoc[http_2 (Http2Config)]
    + - xref:{rootdir}/config/io_helidon_webserver_grpc_GrpcConfig.adoc[grpc (GrpcConfig)]
    + - xref:{rootdir}/config/io_helidon_webserver_websocket_WsConfig.adoc[websocket (WsConfig)]
    + - xref:{rootdir}/config/io_helidon_webserver_http1_Http1Config.adoc[http_1_1 (Http1Config)]
    +
    + |{nbsp} |Configuration of protocols. This may be either protocol selectors, or protocol upgraders from HTTP/1.1.
      As the order is not important (providers are ordered by weight by default), we can use a configuration as an object,
      such as:
      
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc
    index 9531104cc8b..c57c843420d 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsConfig.adoc
    @@ -66,7 +66,7 @@ This type provides the following service implementations:
     |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets.
     
      Socket names to register on, defaults to empty (all available sockets)
    -|`weight` |double |`950.0` |Weight of the CORS feature. As it is used by other features, the default is quite high:
    +|`weight` |double |`850.0` |Weight of the CORS feature. As it is used by other features, the default is quite high:
      CorsFeature.WEIGHT.
     
      Weight of the feature
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc
    index 9531104cc8b..c57c843420d 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_cors_CorsFeature.adoc
    @@ -66,7 +66,7 @@ This type provides the following service implementations:
     |`sockets` |string[] |{nbsp} |List of sockets to register this feature on. If empty, it would get registered on all sockets.
     
      Socket names to register on, defaults to empty (all available sockets)
    -|`weight` |double |`950.0` |Weight of the CORS feature. As it is used by other features, the default is quite high:
    +|`weight` |double |`850.0` |Weight of the CORS feature. As it is used by other features, the default is quite high:
      CorsFeature.WEIGHT.
     
      Weight of the feature
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_grpc_GrpcConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_grpc_GrpcConfig.adoc
    index 5df77b1b502..87d5f506f09 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_grpc_GrpcConfig.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_grpc_GrpcConfig.adoc
    @@ -1,6 +1,6 @@
     ///////////////////////////////////////////////////////////////////////////////
     
    -    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,10 +30,16 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.grpc/io/helidon/webserver/grpc/GrpcConfig.html[io.helidon.webserver.grpc.GrpcConfig]
     
     
    +[source,text]
    +.Config key
    +----
    +grpc
    +----
    +
     
     This type provides the following service implementations:
     
    -- `io.helidon.webserver.spi.ProtocolConfig`
    +- `io.helidon.webserver.spi.ProtocolConfigProvider`
     
     
     == Configuration options
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc
    index 7a8b58ea104..a72c8fbf80c 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_http1_Http1Config.adoc
    @@ -30,10 +30,16 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.http1/io/helidon/webserver/http1/Http1Config.html[io.helidon.webserver.http1.Http1Config]
     
     
    +[source,text]
    +.Config key
    +----
    +http_1_1
    +----
    +
     
     This type provides the following service implementations:
     
    -- `io.helidon.webserver.spi.ProtocolConfig`
    +- `io.helidon.webserver.spi.ProtocolConfigProvider`
     
     
     == Configuration options
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc
    index c6324a3b995..099e63003e3 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_http2_Http2Config.adoc
    @@ -30,10 +30,16 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.http2/io/helidon/webserver/http2/Http2Config.html[io.helidon.webserver.http2.Http2Config]
     
     
    +[source,text]
    +.Config key
    +----
    +http_2
    +----
    +
     
     This type provides the following service implementations:
     
    -- `io.helidon.webserver.spi.ProtocolConfig`
    +- `io.helidon.webserver.spi.ProtocolConfigProvider`
     
     
     == Configuration options
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc
    index 16f38a02e30..2559f02848a 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_ObserveFeature.adoc
    @@ -70,10 +70,10 @@ This type provides the following service implementations:
     
     Such as:
     
    - - xref:{rootdir}/config/io_helidon_webserver_observe_log_LogObserver.adoc[LogObserver]
    - - xref:{rootdir}/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc[TracingObserver]
    - - xref:{rootdir}/config/io_helidon_webserver_observe_config_ConfigObserver.adoc[ConfigObserver]
    - - xref:{rootdir}/config/io_helidon_webserver_observe_info_InfoObserver.adoc[InfoObserver]
    + - xref:{rootdir}/config/io_helidon_webserver_observe_log_LogObserver.adoc[log (LogObserver)]
    + - xref:{rootdir}/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc[tracing (TracingObserver)]
    + - xref:{rootdir}/config/io_helidon_webserver_observe_config_ConfigObserver.adoc[config (ConfigObserver)]
    + - xref:{rootdir}/config/io_helidon_webserver_observe_info_InfoObserver.adoc[info (InfoObserver)]
      - xref:{rootdir}/config/io_helidon_webserver_observe_metrics_MetricsObserver.adoc[metrics (MetricsObserver)]
      - xref:{rootdir}/config/io_helidon_webserver_observe_health_HealthObserver.adoc[health (HealthObserver)]
     
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc
    index 43eb920fc73..ed70f806692 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_config_ConfigObserver.adoc
    @@ -30,6 +30,12 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.observe.config/io/helidon/webserver/observe/config/ConfigObserver.html[io.helidon.webserver.observe.config.ConfigObserver]
     
     
    +[source,text]
    +.Config key
    +----
    +config
    +----
    +
     
     This type provides the following service implementations:
     
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc
    index 4016a71d1af..e56c148a15a 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_info_InfoObserver.adoc
    @@ -30,6 +30,12 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.observe.info/io/helidon/webserver/observe/info/InfoObserver.html[io.helidon.webserver.observe.info.InfoObserver]
     
     
    +[source,text]
    +.Config key
    +----
    +info
    +----
    +
     
     This type provides the following service implementations:
     
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc
    index 69402e92a7e..ff1d897bf07 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_log_LogObserver.adoc
    @@ -30,6 +30,12 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.observe.log/io/helidon/webserver/observe/log/LogObserver.html[io.helidon.webserver.observe.log.LogObserver]
     
     
    +[source,text]
    +.Config key
    +----
    +log
    +----
    +
     
     This type provides the following service implementations:
     
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc
    index fc0e267850e..feea2ca6abc 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_observe_tracing_TracingObserver.adoc
    @@ -30,6 +30,12 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.observe.tracing/io/helidon/webserver/observe/tracing/TracingObserver.html[io.helidon.webserver.observe.tracing.TracingObserver]
     
     
    +[source,text]
    +.Config key
    +----
    +tracing
    +----
    +
     
     This type provides the following service implementations:
     
    diff --git a/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc b/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc
    index 884ee43c457..304bd316def 100644
    --- a/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc
    +++ b/docs/src/main/asciidoc/config/io_helidon_webserver_websocket_WsConfig.adoc
    @@ -30,10 +30,16 @@ include::{rootdir}/includes/attributes.adoc[]
     Type: link:{javadoc-base-url}/io.helidon.webserver.websocket/io/helidon/webserver/websocket/WsConfig.html[io.helidon.webserver.websocket.WsConfig]
     
     
    +[source,text]
    +.Config key
    +----
    +websocket
    +----
    +
     
     This type provides the following service implementations:
     
    -- `io.helidon.webserver.spi.ProtocolConfig`
    +- `io.helidon.webserver.spi.ProtocolConfigProvider`
     
     
     == Configuration options
    diff --git a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcConfigBlueprint.java b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcConfigBlueprint.java
    index 286a8111bee..0593ee88fad 100644
    --- a/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcConfigBlueprint.java
    +++ b/webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcConfigBlueprint.java
    @@ -18,10 +18,11 @@
     
     import io.helidon.builder.api.Prototype;
     import io.helidon.webserver.spi.ProtocolConfig;
    +import io.helidon.webserver.spi.ProtocolConfigProvider;
     
     @Prototype.Blueprint
    -@Prototype.Configured
    -@Prototype.Provides(ProtocolConfig.class)
    +@Prototype.Configured(root = false, value = GrpcProtocolProvider.CONFIG_NAME)
    +@Prototype.Provides(ProtocolConfigProvider.class)
     interface GrpcConfigBlueprint extends ProtocolConfig {
         /**
          * Protocol configuration type.
    diff --git a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ConfigBlueprint.java b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ConfigBlueprint.java
    index b06301dbbdc..d1fe59ed0c0 100644
    --- a/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ConfigBlueprint.java
    +++ b/webserver/http2/src/main/java/io/helidon/webserver/http2/Http2ConfigBlueprint.java
    @@ -22,13 +22,14 @@
     import io.helidon.builder.api.Prototype;
     import io.helidon.http.RequestedUriDiscoveryContext;
     import io.helidon.webserver.spi.ProtocolConfig;
    +import io.helidon.webserver.spi.ProtocolConfigProvider;
     
     /**
      * HTTP/2 server configuration.
      */
     @Prototype.Blueprint(decorator = Http2ConfigBlueprint.Http2ConfigDecorator.class)
    -@Prototype.Configured
    -@Prototype.Provides(ProtocolConfig.class)
    +@Prototype.Configured(root = false, value = Http2ConnectionProvider.CONFIG_NAME)
    +@Prototype.Provides(ProtocolConfigProvider.class)
     interface Http2ConfigBlueprint extends ProtocolConfig {
         /**
          * The size of the largest frame payload that the sender is willing to receive in bytes.
    diff --git a/webserver/observe/config/src/main/java/io/helidon/webserver/observe/config/ConfigObserverConfigBlueprint.java b/webserver/observe/config/src/main/java/io/helidon/webserver/observe/config/ConfigObserverConfigBlueprint.java
    index 4c1f15c3bf1..32bf09fba46 100644
    --- a/webserver/observe/config/src/main/java/io/helidon/webserver/observe/config/ConfigObserverConfigBlueprint.java
    +++ b/webserver/observe/config/src/main/java/io/helidon/webserver/observe/config/ConfigObserverConfigBlueprint.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.
    @@ -24,7 +24,7 @@
     import io.helidon.webserver.observe.spi.ObserveProvider;
     
     @Prototype.Blueprint
    -@Prototype.Configured
    +@Prototype.Configured(root = false, value = "config")
     @Prototype.Provides(ObserveProvider.class)
     interface ConfigObserverConfigBlueprint extends ObserverConfigBase, Prototype.Factory {
         @Option.Configured
    diff --git a/webserver/observe/info/src/main/java/io/helidon/webserver/observe/info/InfoObserverConfigBlueprint.java b/webserver/observe/info/src/main/java/io/helidon/webserver/observe/info/InfoObserverConfigBlueprint.java
    index f6f8a2f4e70..7f66920251d 100644
    --- a/webserver/observe/info/src/main/java/io/helidon/webserver/observe/info/InfoObserverConfigBlueprint.java
    +++ b/webserver/observe/info/src/main/java/io/helidon/webserver/observe/info/InfoObserverConfigBlueprint.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,7 +27,7 @@
      * Info Observer configuration.
      */
     @Prototype.Blueprint
    -@Prototype.Configured
    +@Prototype.Configured(root = false, value = "info")
     @Prototype.Provides(ObserveProvider.class)
     interface InfoObserverConfigBlueprint extends ObserverConfigBase, Prototype.Factory {
         @Option.Configured
    diff --git a/webserver/observe/log/src/main/java/io/helidon/webserver/observe/log/LogObserverConfigBlueprint.java b/webserver/observe/log/src/main/java/io/helidon/webserver/observe/log/LogObserverConfigBlueprint.java
    index b91a384eab1..fbb689e849d 100644
    --- a/webserver/observe/log/src/main/java/io/helidon/webserver/observe/log/LogObserverConfigBlueprint.java
    +++ b/webserver/observe/log/src/main/java/io/helidon/webserver/observe/log/LogObserverConfigBlueprint.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.
    @@ -25,7 +25,7 @@
      * Log Observer configuration.
      */
     @Prototype.Blueprint
    -@Prototype.Configured
    +@Prototype.Configured(root = false, value = "log")
     @Prototype.Provides(ObserveProvider.class)
     interface LogObserverConfigBlueprint extends ObserverConfigBase, Prototype.Factory {
         @Option.Configured
    diff --git a/webserver/observe/tracing/pom.xml b/webserver/observe/tracing/pom.xml
    index 355ea4a973e..283e5266bce 100644
    --- a/webserver/observe/tracing/pom.xml
    +++ b/webserver/observe/tracing/pom.xml
    @@ -50,6 +50,11 @@
                 helidon-common-features-api
                 true
             
    +        
    +            io.helidon.config
    +            helidon-config-metadata
    +            true
    +        
             
                 io.helidon.webserver.testing.junit5
                 helidon-webserver-testing-junit5
    diff --git a/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserverConfigBlueprint.java b/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserverConfigBlueprint.java
    index b2f1c5cef64..b7d3a3ba084 100644
    --- a/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserverConfigBlueprint.java
    +++ b/webserver/observe/tracing/src/main/java/io/helidon/webserver/observe/tracing/TracingObserverConfigBlueprint.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.
    @@ -33,7 +33,7 @@
      * @see io.helidon.webserver.observe.tracing.TracingObserver#builder()
      */
     @Prototype.Blueprint(decorator = TracingObserverSupport.TracingObserverDecorator.class)
    -@Prototype.Configured
    +@Prototype.Configured(root = false, value = "tracing")
     @Prototype.Provides(ObserveProvider.class)
     interface TracingObserverConfigBlueprint extends ObserverConfigBase, Prototype.Factory {
         @Option.Default("tracing")
    @@ -60,6 +60,7 @@ interface TracingObserverConfigBlueprint extends ObserverConfigBase, Prototype.F
          *
          * By default we disable both the SE-style paths ({@code /observe/health}) and the MP-style paths ({@code /health}).
          */
    +
         /**
          * Path specific configuration of tracing.
          *
    diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ConfigBlueprint.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ConfigBlueprint.java
    index 93f38aa76bb..f7673a98a2a 100644
    --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ConfigBlueprint.java
    +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ConfigBlueprint.java
    @@ -22,13 +22,14 @@
     import io.helidon.builder.api.Prototype;
     import io.helidon.http.RequestedUriDiscoveryContext;
     import io.helidon.webserver.spi.ProtocolConfig;
    +import io.helidon.webserver.spi.ProtocolConfigProvider;
     
     /**
      * HTTP/1.1 server configuration.
      */
     @Prototype.Blueprint(decorator = Http1BuilderDecorator.class)
    -@Prototype.Configured
    -@Prototype.Provides(ProtocolConfig.class)
    +@Prototype.Configured(root = false, value = Http1ConnectionProvider.CONFIG_NAME)
    +@Prototype.Provides(ProtocolConfigProvider.class)
     interface Http1ConfigBlueprint extends ProtocolConfig {
         /**
          * Name of this configuration, in most cases the same as {@link #type()}.
    diff --git a/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsConfigBlueprint.java b/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsConfigBlueprint.java
    index a354775fc37..29594b146e4 100644
    --- a/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsConfigBlueprint.java
    +++ b/webserver/websocket/src/main/java/io/helidon/webserver/websocket/WsConfigBlueprint.java
    @@ -21,13 +21,14 @@
     import io.helidon.builder.api.Option;
     import io.helidon.builder.api.Prototype;
     import io.helidon.webserver.spi.ProtocolConfig;
    +import io.helidon.webserver.spi.ProtocolConfigProvider;
     
     /**
      * WebSocket protocol configuration.
      */
     @Prototype.Blueprint
    -@Prototype.Configured
    -@Prototype.Provides(ProtocolConfig.class)
    +@Prototype.Configured(root = false, value = WsUpgradeProvider.CONFIG_NAME)
    +@Prototype.Provides(ProtocolConfigProvider.class)
     interface WsConfigBlueprint extends ProtocolConfig {
         /**
          * WebSocket origins.
    
    From 5343142f54a1ccaa7d19c0dfebeccadd35e800a6 Mon Sep 17 00:00:00 2001
    From: Tomas Langer 
    Date: Tue, 18 Jun 2024 11:46:21 +0200
    Subject: [PATCH 055/245] 4.x: Improved parsing of HTTP/1 prologue and headers.
     (#8890)
    
    Using SWAR (SIMD within a register) batch read technique to optimize lookup when parsing HTTP/1.1 prologue and headers.
    
    This technique allows searching the bytes by long words (8 bytes per cycle), instead of one by one. This improves speed, as it eliminates some bound checks, for cycles, and branches from the code.
    We may get additional improvement in the future by using vectors for SIMD parallelization (now an incubator feature in JDK).
    ---
     .../java/io/helidon/common/buffers/Bytes.java | 157 ++++++++++++++-
     .../io/helidon/common/buffers/DataReader.java |  85 +++++++-
     .../io/helidon/common/buffers/LazyString.java |  21 +-
     .../java/io/helidon/http/HeadersImpl.java     |   4 +-
     .../io/helidon/http/Http1HeadersParser.java   |  75 ++++++-
     .../webserver/benchmark/jmh/HuJmhTest.java    |  59 ------
     .../benchmark/jmh/ParsingJmhTest.java         |  75 +++++++
     .../webserver/http1/Http1Prologue.java        | 188 ++++++++++++------
     8 files changed, 516 insertions(+), 148 deletions(-)
     delete mode 100644 tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/HuJmhTest.java
     create mode 100644 tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/ParsingJmhTest.java
    
    diff --git a/common/buffers/src/main/java/io/helidon/common/buffers/Bytes.java b/common/buffers/src/main/java/io/helidon/common/buffers/Bytes.java
    index f6ec7a596d1..b7153718d9a 100644
    --- a/common/buffers/src/main/java/io/helidon/common/buffers/Bytes.java
    +++ b/common/buffers/src/main/java/io/helidon/common/buffers/Bytes.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2022 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,6 +16,16 @@
     
     package io.helidon.common.buffers;
     
    +import java.nio.ByteOrder;
    +
    +/*
    + * SWAR related methods in this class are heavily inspired by:
    + * PR https://github.com/netty/netty/pull/10737
    + *
    + * From Netty
    + * Distributed under Apache License, Version 2.0
    + */
    +
     /**
      * Bytes commonly used in HTTP.
      */
    @@ -69,6 +79,151 @@ public final class Bytes {
          */
         public static final byte TAB_BYTE = (byte) '\t';
     
    +    private static final boolean BYTE_ORDER_LE = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
    +
         private Bytes() {
         }
    +
    +    /**
    +     * This is using a SWAR (SIMD Within A Register) batch read technique to minimize bound-checks and improve memory
    +     * usage while searching for {@code value}.
    +     * 

    + * This method does NOT do a bound check on the buffer length and the fromIndex and toIndex, neither does it check + * if the {@code toIndex} is bigger than the {@code fromIndex}. + * + * @param buffer the byte buffer to search + * @param fromIndex first index in the array + * @param toIndex last index in the array + * @param value to search for + * @return first index of the desired byte {@code value}, or {@code -1} if not found + */ + public static int firstIndexOf(byte[] buffer, int fromIndex, int toIndex, byte value) { + if (fromIndex == toIndex || buffer.length == 0) { + // fast path for empty buffers, or empty range + return -1; + } + int length = toIndex - fromIndex; + int offset = fromIndex; + int byteCount = length & 7; + if (byteCount > 0) { + int index = unrolledFirstIndexOf(buffer, fromIndex, byteCount, value); + if (index != -1) { + return index; + } + offset += byteCount; + if (offset == toIndex) { + return -1; + } + } + int longCount = length >>> 3; + long pattern = compilePattern(value); + for (int i = 0; i < longCount; i++) { + // use the faster available getLong + long word = toWord(buffer, offset); + int index = firstInstance(word, pattern); + if (index < Long.BYTES) { + return offset + index; + } + offset += Long.BYTES; + } + return -1; + } + + /** + * Converts the first 8 bytes from {@code offset} to a long, using appropriate byte order for this machine. + *

      + *
    • This method DOES NOT do a bound check
    • + *
    • This method DOES NOT validate there are 8 bytes available
    • + *
    + * + * @param buffer bytes to convert + * @param offset offset within the byte array + * @return long word from the first 8 bytes from offset + */ + public static long toWord(byte[] buffer, int offset) { + return BYTE_ORDER_LE ? toWordLe(buffer, offset) : toWordBe(buffer, offset); + } + + // create a pattern for a byte, so we can search for it in a whole word + private static long compilePattern(byte byteToFind) { + return (byteToFind & 0xFFL) * 0x101010101010101L; + } + + // first instance of a pattern within a word (see above compilePattern) + private static int firstInstance(long word, long pattern) { + long input = word ^ pattern; + long tmp = (input & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL; + tmp = ~(tmp | input | 0x7F7F7F7F7F7F7F7FL); + int binaryPosition = Long.numberOfTrailingZeros(tmp); + return binaryPosition >>> 3; + } + + // to a little endian word + private static long toWordLe(byte[] buffer, int index) { + return (long) buffer[index] & 0xff + | ((long) buffer[index + 1] & 0xff) << 8 + | ((long) buffer[index + 2] & 0xff) << 16 + | ((long) buffer[index + 3] & 0xff) << 24 + | ((long) buffer[index + 4] & 0xff) << 32 + | ((long) buffer[index + 5] & 0xff) << 40 + | ((long) buffer[index + 6] & 0xff) << 48 + | ((long) buffer[index + 7] & 0xff) << 56; + } + + private static long toWordBe(byte[] buffer, int index) { + return ((long) buffer[index] & 0xff) << 56 + | ((long) buffer[index + 1] & 0xff) << 48 + | ((long) buffer[index + 2] & 0xff) << 40 + | ((long) buffer[index + 3] & 0xff) << 32 + | ((long) buffer[index + 4] & 0xff) << 24 + | ((long) buffer[index + 5] & 0xff) << 16 + | ((long) buffer[index + 6] & 0xff) << 8 + | (long) buffer[index + 7] & 0xff; + } + + // this method is copied from Netty, and validated by them that it is the optimal + // way to figure out the index, see https://github.com/netty/netty/issues/10731 + private static int unrolledFirstIndexOf(byte[] buffer, int fromIndex, int byteCount, byte value) { + assert byteCount > 0 && byteCount < 8; + if (buffer[fromIndex] == value) { + return fromIndex; + } + if (byteCount == 1) { + return -1; + } + if (buffer[fromIndex + 1] == value) { + return fromIndex + 1; + } + if (byteCount == 2) { + return -1; + } + if (buffer[fromIndex + 2] == value) { + return fromIndex + 2; + } + if (byteCount == 3) { + return -1; + } + if (buffer[fromIndex + 3] == value) { + return fromIndex + 3; + } + if (byteCount == 4) { + return -1; + } + if (buffer[fromIndex + 4] == value) { + return fromIndex + 4; + } + if (byteCount == 5) { + return -1; + } + if (buffer[fromIndex + 5] == value) { + return fromIndex + 5; + } + if (byteCount == 6) { + return -1; + } + if (buffer[fromIndex + 6] == value) { + return fromIndex + 6; + } + return -1; + } } diff --git a/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java b/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java index 0777387ceb8..f2402fb75c9 100644 --- a/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java +++ b/common/buffers/src/main/java/io/helidon/common/buffers/DataReader.java @@ -289,6 +289,36 @@ public String readAsciiString(int len) { } } + /** + * Read byte array. + * + * @param len number of bytes of the string + * @return string value + */ + public byte[] readBytes(int len) { + ensureAvailable(); // we have at least 1 byte + byte[] b = new byte[len]; + + if (len <= head.available()) { // fast case + System.arraycopy(head.bytes, head.position, b, 0, len); + head.position += len; + return b; + } else { + int remaining = len; + for (Node n = head; remaining > 0; n = n.next) { + ensureAvailable(); + int toAdd = Math.min(remaining, n.available()); + System.arraycopy(n.bytes, n.position, b, len - remaining, toAdd); + remaining -= toAdd; + n.position += toAdd; + if (remaining > 0 && n.next == null) { + pullData(); + } + } + return b; + } + } + /** * Read an ascii string until new line. * @@ -367,31 +397,64 @@ public int findNewLine(int max) throws IncorrectNewLineException { ensureAvailable(); int idx = 0; Node n = head; + int indexWithinNode = n.position; + while (true) { byte[] barr = n.bytes; - for (int i = n.position; i < barr.length && idx < max; i++, idx++) { - if (barr[i] == Bytes.LF_BYTE && !ignoreLoneEol) { - throw new IncorrectNewLineException("Found LF (" + idx + ") without preceding CR. :\n" + this.debugDataHex()); - } else if (barr[i] == Bytes.CR_BYTE) { - byte nextByte; - if (i + 1 < barr.length) { - nextByte = barr[i + 1]; - } else { - nextByte = n.next().peek(); + int maxLength = Math.min(max - idx, barr.length - indexWithinNode); + int crIndex = Bytes.firstIndexOf(barr, indexWithinNode, indexWithinNode + maxLength, Bytes.CR_BYTE); + + if (crIndex == -1) { + int lfIndex = Bytes.firstIndexOf(barr, indexWithinNode, indexWithinNode + maxLength, Bytes.LF_BYTE); + if (lfIndex != -1) { + if (!ignoreLoneEol) { + throw new IncorrectNewLineException("Found LF (" + (idx + lfIndex - n.position) + + ") without preceding CR. :\n" + this.debugDataHex()); } + } + // not found, continue with next buffer + idx += maxLength; + if (idx >= max) { + // not found and reached the limit + return max; + } + n = n.next(); + indexWithinNode = n.position; + continue; + } else { + // found, next byte should be LF + if (crIndex == barr.length - 1) { + // found CR as the last byte of the current node, peek next node + byte nextByte = n.next().peek(); if (nextByte == Bytes.LF_BYTE) { - return idx; + return idx + crIndex - n.position; + } + if (!ignoreLoneEol) { + throw new IncorrectNewLineException("Found CR (" + (idx + crIndex - n.position) + + ") without following LF. :\n" + this.debugDataHex()); + } + } else { + // found CR within the current array + byte nextByte = barr[crIndex + 1]; + if (nextByte == Bytes.LF_BYTE) { + return idx + crIndex - n.position; } if (!ignoreLoneEol) { throw new IncorrectNewLineException("Found CR (" + idx + ") without following LF. :\n" + this.debugDataHex()); } + indexWithinNode = crIndex + 1; + idx += indexWithinNode; + continue; } } - if (idx == max) { + + idx += maxLength; + if (idx >= max) { return max; } n = n.next(); + indexWithinNode = n.position; } } diff --git a/common/buffers/src/main/java/io/helidon/common/buffers/LazyString.java b/common/buffers/src/main/java/io/helidon/common/buffers/LazyString.java index 4843e6bac84..3e5ea5e928b 100644 --- a/common/buffers/src/main/java/io/helidon/common/buffers/LazyString.java +++ b/common/buffers/src/main/java/io/helidon/common/buffers/LazyString.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. @@ -22,6 +22,12 @@ * String that materializes only when requested. */ public class LazyString { + private static final boolean[] IS_OWS = new boolean[256]; + static { + IS_OWS[Bytes.SPACE_BYTE] = true; + IS_OWS[Bytes.TAB_BYTE] = true; + } + private final byte[] buffer; private final int offset; private final int length; @@ -69,9 +75,9 @@ public String stripOws() { int newOffset = offset; int newLength = length; for (int i = offset; i < offset + length; i++) { - if (isOws(buffer[i])) { + if (IS_OWS[buffer[i] & 0xff]) { newOffset = i + 1; - newLength = length - (newOffset - offset); + newLength--; } else { // no more white space, go from the end now break; @@ -79,7 +85,7 @@ public String stripOws() { } // now we need to go from the end of the string for (int i = offset + length - 1; i > newOffset; i--) { - if (isOws(buffer[i])) { + if (IS_OWS[buffer[i] & 0xff]) { newLength--; } else { break; @@ -99,11 +105,4 @@ public String toString() { } return stringValue; } - - private boolean isOws(byte aByte) { - return switch (aByte) { - case Bytes.SPACE_BYTE, Bytes.TAB_BYTE -> true; - default -> false; - }; - } } diff --git a/http/http/src/main/java/io/helidon/http/HeadersImpl.java b/http/http/src/main/java/io/helidon/http/HeadersImpl.java index f11c6de17a6..cc730b62fc0 100644 --- a/http/http/src/main/java/io/helidon/http/HeadersImpl.java +++ b/http/http/src/main/java/io/helidon/http/HeadersImpl.java @@ -162,7 +162,9 @@ public T set(Header header) { HeaderName name = header.headerName(); Header usedHeader = header; - if (header instanceof HeaderWriteable) { + if (header instanceof HeaderValueLazy) { + // use it directly (lazy values are write once) + } else if (header instanceof HeaderWriteable) { // we must create a new instance, as we risk modifying state of the provided header usedHeader = new HeaderValueCopy(header); } diff --git a/http/http/src/main/java/io/helidon/http/Http1HeadersParser.java b/http/http/src/main/java/io/helidon/http/Http1HeadersParser.java index 4a7ebb1878b..4620b4a838b 100644 --- a/http/http/src/main/java/io/helidon/http/Http1HeadersParser.java +++ b/http/http/src/main/java/io/helidon/http/Http1HeadersParser.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. @@ -26,14 +26,29 @@ * Used by both HTTP server and client to parse headers from {@link io.helidon.common.buffers.DataReader}. */ public final class Http1HeadersParser { - // TODO expand set of fastpath headers private static final byte[] HD_HOST = (HeaderNameEnum.HOST.defaultCase() + ":").getBytes(StandardCharsets.UTF_8); private static final byte[] HD_ACCEPT = (HeaderNameEnum.ACCEPT.defaultCase() + ":").getBytes(StandardCharsets.UTF_8); private static final byte[] HD_CONNECTION = (HeaderNameEnum.CONNECTION.defaultCase() + ":").getBytes(StandardCharsets.UTF_8); - private static final byte[] HD_USER_AGENT = (HeaderNameEnum.USER_AGENT.defaultCase() + ":").getBytes(StandardCharsets.UTF_8); + private static final long CLOSE_WORD = ' ' + | 'c' << 8 + | 'l' << 16 + | 'o' << 24 + | (long) 's' << 32 + | (long) 'e' << 40; + private static final long KEEP_ALIVE_WORD_1 = ' ' + | 'k' << 8 + | 'e' << 16 + | 'e' << 24 + | (long) 'p' << 32 + | (long) '-' << 40 + | (long) 'a' << 48 + | (long) 'l' << 56; + private static final long KEEP_ALIVE_WORD_2 = 'i' + | 'v' << 8 + | 'e' << 16; private Http1HeadersParser() { } @@ -56,18 +71,24 @@ public static WritableHeaders readHeaders(DataReader reader, int maxHeadersSi return headers; } - HeaderName header = readHeaderName(reader, maxLength, validate); + HeaderName header = readHeaderName(reader, maxLength); maxLength -= header.defaultCase().length() + 2; int eol = reader.findNewLine(maxLength); if (eol == maxLength) { throw new IllegalStateException("Header size exceeded"); } - // we do not need the string until somebody asks for this header (unless validation is on) - LazyString value = reader.readLazyString(StandardCharsets.US_ASCII, eol); + // optimization for Connection, as we always need to analyze it + Header headerValue; + if (header.equals(HeaderNames.CONNECTION)) { + headerValue = connectionHeaderValue(reader, eol); + } else { + // we do not need the string until somebody asks for this header (unless validation is on) + LazyString value = reader.readLazyString(StandardCharsets.US_ASCII, eol); + headerValue = HeaderValues.create(header, value); + } reader.skip(2); maxLength -= eol + 1; - Header headerValue = HeaderValues.create(header, value); headers.add(headerValue); if (validate) { headerValue.validate(); @@ -78,9 +99,45 @@ public static WritableHeaders readHeaders(DataReader reader, int maxHeadersSi } } + private static Header connectionHeaderValue(DataReader reader, int eol) { + byte[] bytes = reader.readBytes(eol); + + // space and `keep-alive` + if (bytes.length == 11) { + if (isKeepAlive(bytes)) { + return HeaderValues.CONNECTION_KEEP_ALIVE; + } + } + // space and `close` + if (bytes.length == 6 && isClose(bytes)) { + return HeaderValues.CONNECTION_CLOSE; + } + // some other (unexpected) combination + return HeaderValues.create(HeaderNames.CONNECTION, new LazyString(bytes, 0, bytes.length, StandardCharsets.US_ASCII)); + } + + private static boolean isKeepAlive(byte[] buffer) { + if (Bytes.toWord(buffer, 0) != KEEP_ALIVE_WORD_1) { + return false; + } + long endWord = buffer[8] & 0xff + | (buffer[9] & 0xff) << 8 + | (buffer[10] & 0xff) << 16; + return endWord == KEEP_ALIVE_WORD_2; + } + + private static boolean isClose(byte[] buffer) { + long word = buffer[0] & 0xff + | (buffer[1] & 0xff) << 8 + | (buffer[2] & 0xff) << 16 + | ((long) buffer[3] & 0xff) << 24 + | ((long) buffer[4] & 0xff) << 32 + | ((long) buffer[5] & 0xff) << 40; + return word == CLOSE_WORD; + } + private static HeaderName readHeaderName(DataReader reader, - int maxLength, - boolean validate) { + int maxLength) { switch (reader.lookup()) { case (byte) 'H' -> { if (reader.startsWith(HD_HOST)) { diff --git a/tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/HuJmhTest.java b/tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/HuJmhTest.java deleted file mode 100644 index 2e518132545..00000000000 --- a/tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/HuJmhTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, 2023 Oracle and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.helidon.webserver.benchmark.jmh; - -import java.util.Base64; -import java.util.Random; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.infra.Blackhole; - -@State(Scope.Benchmark) -public class HuJmhTest { - private String strOne; - private String strTwo; - - @Setup - public void setup() { - byte[] rand = new byte[26]; - new Random().nextBytes(rand); - String str = Base64.getEncoder().encodeToString(rand); - strOne = "/" + str; - strTwo = str; - } - - @Benchmark - public void startsWith(Blackhole bh) { - boolean one = strOne.startsWith("/"); - boolean two = strTwo.startsWith("/"); - - bh.consume(one); - bh.consume(two); - } - - @Benchmark - public void charAt(Blackhole bh) { - boolean one = strOne.charAt(0) == '/'; - boolean two = strTwo.charAt(0) == '/'; - - bh.consume(one); - bh.consume(two); - } -} diff --git a/tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/ParsingJmhTest.java b/tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/ParsingJmhTest.java new file mode 100644 index 00000000000..857743d38c0 --- /dev/null +++ b/tests/benchmark/jmh/src/main/java/io/helidon/webserver/benchmark/jmh/ParsingJmhTest.java @@ -0,0 +1,75 @@ +/* + * 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. + * 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.webserver.benchmark.jmh; + +import java.nio.charset.StandardCharsets; + +import io.helidon.common.buffers.DataReader; +import io.helidon.http.HttpPrologue; +import io.helidon.http.Method; +import io.helidon.http.WritableHeaders; +import io.helidon.webserver.http1.Http1Headers; +import io.helidon.webserver.http1.Http1Prologue; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; + +/* +These numbers are irrelevant on a different machine. This is just a not on improvements done by SWAR used in parsing. +Prologue +original 5633378.812 ± 261834.000 ops/s +new 6842537.897 ± 109193.680 ops/s + +Headers +original 2930640.115 ± 39855.689 ops/s +new 5342407.893 ops/s + */ +@State(Scope.Benchmark) +public class ParsingJmhTest { + private static final byte[] PROLOGUE_WITH_EOL = "POST /some/path?query=some_query_message_text HTTP/1.1\r\n" + .getBytes(StandardCharsets.US_ASCII); + private static final HttpPrologue PROLOGUE = HttpPrologue.create("HTTP", "HTTP", "1.1", Method.GET, "/", false); + private static final byte[] HEADERS = """ + Host: localhost:8080\r + User-Agent: curl/7.68.0\r + Connection: keep-alive\r + Accept: text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7\r + \r + """.getBytes(StandardCharsets.US_ASCII); + + @Benchmark + public void prologue(Blackhole bh) { + DataReader reader = new DataReader(() -> PROLOGUE_WITH_EOL); + Http1Prologue p = new Http1Prologue(reader, 1024, false); + HttpPrologue httpPrologue = p.readPrologue(); + bh.consume(httpPrologue.method()); + bh.consume(httpPrologue.query()); + bh.consume(httpPrologue.uriPath()); + bh.consume(httpPrologue); + } + + @Benchmark + public void headers(Blackhole bh) { + DataReader reader = new DataReader(() -> HEADERS); + Http1Headers p = new Http1Headers(reader, 1024, false); + WritableHeaders headers = p.readHeaders(PROLOGUE); + + bh.consume(headers); + } +} diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Prologue.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Prologue.java index 8ad76e03c1d..29d53e44c54 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Prologue.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Prologue.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,7 +24,6 @@ import io.helidon.http.HttpPrologue; import io.helidon.http.Method; import io.helidon.http.RequestException; -import io.helidon.http.Status; import io.helidon.webserver.CloseConnectionException; import io.helidon.webserver.http.DirectTransportRequest; @@ -32,7 +31,37 @@ * HTTP 1 prologue parsing support. */ public final class Http1Prologue { - private static final byte[] GET_BYTES = "GET ".getBytes(StandardCharsets.UTF_8); // space included + /* + The string HTTP/1.1 (as used in protocol version in prologue's third section) + */ + private static final long HTTP_1_1_LONG = 'H' + | 'T' << 8 + | 'T' << 16 + | 'P' << 24 + | (long) '/' << 32 + | (long) '1' << 40 + | (long) '.' << 48 + | (long) '1' << 56; + /* + GET string as int + */ + private static final int GET_INT = 'G' + | 'E' << 8 + | 'T' << 16; + /* + PUT string as int + */ + private static final int PUT_INT = 'P' + | 'U' << 8 + | 'T' << 16; + /* + POST string as int + */ + private static final int POST_INT = 'P' + | 'O' << 8 + | 'S' << 16 + | 'T' << 24; + private static final String HTTP_1_1 = "HTTP/1.1"; private final DataReader reader; private final int maxLength; @@ -79,63 +108,48 @@ private static RequestException badRequest(String message, String method, String .build(); } - private static Method readMethod(DataReader reader, int maxLen) { - if (reader.startsWith(GET_BYTES)) { - reader.skip(GET_BYTES.length); - return Method.GET; - } - int firstSpace = reader.findOrNewLine(Bytes.SPACE_BYTE, maxLen); - if (firstSpace < 0) { - throw badRequest("Invalid prologue, missing space " + reader.debugDataHex(), "", "", "", ""); - } else if (firstSpace == maxLen) { - throw badRequest("Prologue size exceeded", "", "", "", ""); + private static Method readMethod(byte[] bytes, int index, int spaceIndex) { + int len = spaceIndex - index; + if (len == 3) { + if (isGetMethod(bytes, index)) { + return Method.GET; + } + if (isPutMethod(bytes, index)) { + return Method.PUT; + } + } else if (len == 4 && isPostMethod(bytes, index)) { + return Method.POST; } - Method method = Method.create(reader.readAsciiString(firstSpace)); - reader.skip(1); - return method; + return Method.create(new String(bytes, index, len, StandardCharsets.US_ASCII)); + } + + private static boolean isGetMethod(byte[] bytes, int index) { + int maybeGet = bytes[index] & 0xff + | (bytes[index + 1] & 0xff) << 8 + | (bytes[index + 2] & 0xff) << 16; + return maybeGet == GET_INT; + } + + private static boolean isPutMethod(byte[] bytes, int index) { + int maybeGet = bytes[index] & 0xff + | (bytes[index + 1] & 0xff) << 8 + | (bytes[index + 2] & 0xff) << 16; + return maybeGet == PUT_INT; + } + + private static boolean isPostMethod(byte[] bytes, int index) { + int maybePost = bytes[index] & 0xff + | (bytes[index + 1] & 0xff) << 8 + | (bytes[index + 2] & 0xff) << 16 + | (bytes[index + 3] & 0xff) << 24; + return maybePost == POST_INT; } private HttpPrologue doRead() { - // > GET /loom/slow HTTP/1.1 - String protocol; - String path; - Method method; + int eol; + try { - int maxLen = maxLength; - try { - method = readMethod(reader, maxLen); - } catch (IllegalArgumentException e) { - // we need to validate method contains only allowed characters - throw badRequest("Invalid prologue, method not valid (" + e.getMessage() + ")", "", "", "", ""); - } - maxLen -= method.length(); // length of method - maxLen--; // space - - int secondSpace = reader.findOrNewLine(Bytes.SPACE_BYTE, maxLen); - if (secondSpace < 0) { - throw badRequest("Invalid prologue" + reader.debugDataHex(), method.text(), "", "", ""); - } else if (secondSpace == maxLen) { - throw RequestException.builder() - .message("Request URI too long.") - .type(DirectHandler.EventType.BAD_REQUEST) - .status(Status.REQUEST_URI_TOO_LONG_414) - .request(DirectTransportRequest.create("", method.text(), reader.readAsciiString(secondSpace))) - .build(); - } - path = reader.readAsciiString(secondSpace); - reader.skip(1); - maxLen -= secondSpace; - maxLen--; // space - - int eol = reader.findNewLine(maxLen); - if (eol == maxLen) { - throw badRequest("Prologue size exceeded", method.text(), path, "", ""); - } - if (path.isBlank()) { - throw badRequest("Path can't be empty", method.text(), path, "", ""); - } - protocol = reader.readAsciiString(eol); - reader.skip(2); // \r\n + eol = reader.findNewLine(maxLength); } catch (DataReader.IncorrectNewLineException e) { throw RequestException.builder() .message("Invalid prologue: " + e.getMessage()) @@ -143,8 +157,53 @@ private HttpPrologue doRead() { .cause(e) .build(); } + if (eol == maxLength) { + // exceeded maximal length, we do not want to parse it anyway + throw RequestException.builder() + .message("Prologue size exceeded") + .type(DirectHandler.EventType.BAD_REQUEST) + .build(); + } + + byte[] prologueBytes = reader.readBytes(eol); + reader.skip(2); // \r\n + + // > GET /loom/slow HTTP/1.1 + Method method; + String path; + String protocol; + + /* + Read HTTP Method + */ + int currentIndex = 0; + int nextSpace = nextSpace(prologueBytes, currentIndex); + if (nextSpace == -1) { + throw badRequest("Invalid prologue, missing space " + reader.debugDataHex(), "", "", "", ""); + } + method = readMethod(prologueBytes, currentIndex, nextSpace); + currentIndex = nextSpace + 1; // continue after the space - if (!"HTTP/1.1".equals(protocol)) { + /* + Read HTTP Path + */ + nextSpace = nextSpace(prologueBytes, currentIndex); + if (nextSpace == -1) { + throw badRequest("Invalid prologue, missing space " + reader.debugDataHex(), method.text(), "", "", ""); + } + path = new String(prologueBytes, currentIndex, (nextSpace - currentIndex), StandardCharsets.US_ASCII); + currentIndex = nextSpace + 1; // continue after the space + if (path.isBlank()) { + throw badRequest("Path can't be empty", method.text(), path, "", ""); + } + + /* + Read HTTP Version (we only support HTTP/1.1 + */ + protocol = readProtocol(prologueBytes, currentIndex); + // we always use the same constant + //noinspection StringEquality + if (protocol != HTTP_1_1) { throw badRequest("Invalid protocol and/or version", method.text(), path, protocol, ""); } @@ -159,4 +218,21 @@ private HttpPrologue doRead() { throw badRequest("Invalid path: " + e.getMessage(), method.text(), path, "HTTP", "1.1"); } } + + private int nextSpace(byte[] prologueBytes, int currentIndex) { + return Bytes.firstIndexOf(prologueBytes, currentIndex, prologueBytes.length - 1, Bytes.SPACE_BYTE); + } + + private String readProtocol(byte[] bytes, int index) { + int length = bytes.length - index; + + if (length == 8) { + long word = Bytes.toWord(bytes, index); + if (word == HTTP_1_1_LONG) { + return HTTP_1_1; + } + } + + return new String(bytes, StandardCharsets.US_ASCII); + } } From b13a31c82af0f2635dd0bfbb727af95b5b33f90d Mon Sep 17 00:00:00 2001 From: Joe DiPol Date: Thu, 20 Jun 2024 14:26:47 -0700 Subject: [PATCH 056/245] Bump oci-sdk to 3.43.2 (#8899) --- dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index c4b41226b91..00574eab23d 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -126,7 +126,7 @@ 7.0.0.Final 5.12.0 4.1.108.Final - 3.39.0 + 3.43.2 21 + + 4.0.0 + + io.helidon.integrations.oci.authentication + helidon-integrations-oci-authentication-project + 4.1.0-SNAPSHOT + + helidon-integrations-oci-authentication-instance + Helidon Integrations OCI Authentication Instance + + + Authentication based on Instance's principal + + + + + io.helidon.integrations.oci + helidon-integrations-oci + + + io.helidon.service + helidon-service-registry + + + 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.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + diff --git a/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/AuthenticationMethodInstancePrincipal.java b/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/AuthenticationMethodInstancePrincipal.java new file mode 100644 index 00000000000..0c262c9750d --- /dev/null +++ b/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/AuthenticationMethodInstancePrincipal.java @@ -0,0 +1,74 @@ +/* + * 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.authentication.instance; + +import java.util.Optional; + +import io.helidon.common.LazyValue; +import io.helidon.common.Weight; +import io.helidon.common.Weighted; +import io.helidon.integrations.oci.HelidonOci; +import io.helidon.integrations.oci.OciConfig; +import io.helidon.integrations.oci.spi.OciAuthenticationMethod; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; +import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder; + +/** + * Instance principal authentication method, uses the + * {@link com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider}. + */ +@Weight(Weighted.DEFAULT_WEIGHT - 40) +@Service.Provider +class AuthenticationMethodInstancePrincipal implements OciAuthenticationMethod { + private static final System.Logger LOGGER = System.getLogger(AuthenticationMethodInstancePrincipal.class.getName()); + private static final String METHOD = "instance-principal"; + + private final LazyValue> provider; + + AuthenticationMethodInstancePrincipal(OciConfig config, + InstancePrincipalsAuthenticationDetailsProviderBuilder builder) { + provider = createProvider(config, builder); + } + + @Override + public String method() { + return METHOD; + } + + @Override + public Optional provider() { + return provider.get(); + } + + private static LazyValue> + createProvider(OciConfig config, + InstancePrincipalsAuthenticationDetailsProviderBuilder builder) { + return LazyValue.create(() -> { + if (HelidonOci.imdsAvailable(config)) { + return Optional.of(builder.build()); + } + if (LOGGER.isLoggable(System.Logger.Level.TRACE)) { + LOGGER.log(System.Logger.Level.TRACE, "OCI Metadata service is not available, " + + "instance principal cannot be used."); + } + return Optional.empty(); + }); + } + +} diff --git a/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/InstancePrincipalBuilderProvider.java b/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/InstancePrincipalBuilderProvider.java new file mode 100644 index 00000000000..769715317de --- /dev/null +++ b/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/InstancePrincipalBuilderProvider.java @@ -0,0 +1,52 @@ +/* + * 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.authentication.instance; + +import java.net.URI; +import java.util.function.Supplier; + +import io.helidon.integrations.oci.OciConfig; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider; +import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder; + +/** + * Instance principal builder provider, uses the + * {@link InstancePrincipalsAuthenticationDetailsProviderBuilder}. + */ +@Service.Provider +class InstancePrincipalBuilderProvider implements Supplier { + private final OciConfig config; + + InstancePrincipalBuilderProvider(OciConfig config) { + this.config = config; + } + + @Override + public InstancePrincipalsAuthenticationDetailsProviderBuilder get() { + var builder = InstancePrincipalsAuthenticationDetailsProvider.builder() + .timeoutForEachRetry((int) config.authenticationTimeout().toMillis()); + + config.imdsBaseUri() + .map(URI::toString) + .ifPresent(builder::metadataBaseUrl); + + return builder; + } + +} diff --git a/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/package-info.java b/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/package-info.java new file mode 100644 index 00000000000..f121134bd6b --- /dev/null +++ b/integrations/oci/authentication/instance/src/main/java/io/helidon/integrations/oci/authentication/instance/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. + */ + +/** + * Support for OCI authentication method based on instance principal. + */ +package io.helidon.integrations.oci.authentication.instance; diff --git a/integrations/oci/authentication/instance/src/main/java/module-info.java b/integrations/oci/authentication/instance/src/main/java/module-info.java new file mode 100644 index 00000000000..ea1ab72d12c --- /dev/null +++ b/integrations/oci/authentication/instance/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/** + * Support for OCI authentication method based on instance principal. + */ +module io.helidon.integrations.oci.authentication.instance { + requires io.helidon.common; + requires io.helidon.service.registry; + requires io.helidon.integrations.oci; + + requires oci.java.sdk.common; + + exports io.helidon.integrations.oci.authentication.instance; +} diff --git a/integrations/oci/authentication/oke-workload/pom.xml b/integrations/oci/authentication/oke-workload/pom.xml new file mode 100644 index 00000000000..5fc00f970b6 --- /dev/null +++ b/integrations/oci/authentication/oke-workload/pom.xml @@ -0,0 +1,112 @@ + + + + 4.0.0 + + io.helidon.integrations.oci.authentication + helidon-integrations-oci-authentication-project + 4.1.0-SNAPSHOT + + helidon-integrations-oci-authentication-oke-workload + Helidon Integrations OCI Authentication Workload + + + Authentication based on Workload identity + + + + + io.helidon.integrations.oci + helidon-integrations-oci + + + io.helidon.service + helidon-service-registry + + + com.oracle.oci.sdk + oci-java-sdk-addons-oke-workload-identity + + + 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.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + diff --git a/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/AuthenticationMethodOkeWorkload.java b/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/AuthenticationMethodOkeWorkload.java new file mode 100644 index 00000000000..22405816ef7 --- /dev/null +++ b/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/AuthenticationMethodOkeWorkload.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.integrations.oci.authentication.okeworkload; + +import java.lang.System.Logger.Level; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import io.helidon.common.LazyValue; +import io.helidon.common.Weight; +import io.helidon.common.Weighted; +import io.helidon.integrations.oci.OciConfig; +import io.helidon.integrations.oci.spi.OciAuthenticationMethod; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; +import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider; + +/** + * Instance principal authentication method, uses the + * {@link com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider}. + */ +@Weight(Weighted.DEFAULT_WEIGHT - 40) +@Service.Provider +class AuthenticationMethodOkeWorkload implements OciAuthenticationMethod { + private static final System.Logger LOGGER = System.getLogger(AuthenticationMethodOkeWorkload.class.getName()); + + private static final String METHOD = "oke-workload-identity"; + + /* + These constants are copied from + com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider + as they are not public + */ + private static final String SERVICE_ACCOUNT_CERT_PATH_DEFAULT = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"; + private static final String SERVICE_ACCOUNT_CERT_PATH_ENV = "OCI_KUBERNETES_SERVICE_ACCOUNT_CERT_PATH"; + + private final LazyValue> provider; + + AuthenticationMethodOkeWorkload(OciConfig config) { + provider = createProvider(config); + } + + @Override + public String method() { + return METHOD; + } + + @Override + public Optional provider() { + return provider.get(); + } + + private static LazyValue> createProvider(OciConfig config) { + return LazyValue.create(() -> { + if (available()) { + return Optional.of(OkeWorkloadIdentityAuthenticationDetailsProvider.builder() + .build()); + + } + return Optional.empty(); + }); + } + + /** + * Returns true if the configured (or default) path with OKE account certificate path exists and is a + * regular file. + */ + private static boolean available() { + String usedPath = System.getenv(SERVICE_ACCOUNT_CERT_PATH_ENV); + usedPath = usedPath == null ? SERVICE_ACCOUNT_CERT_PATH_DEFAULT : usedPath; + + Path certPath = Paths.get(usedPath); + + if (!Files.exists(certPath)) { + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, "OKE Workload Authentication Details Provider is not available, as " + + "the certificate file does not exist: " + certPath.toAbsolutePath()); + } + return false; + } + if (Files.isRegularFile(certPath)) { + return true; + } + + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, "OKE Workload Authentication Details Provider is not available, as " + + "the certificate file is not a regular file: " + certPath.toAbsolutePath()); + } + return false; + } +} diff --git a/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/OkeWorkloadBuilderProvider.java b/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/OkeWorkloadBuilderProvider.java new file mode 100644 index 00000000000..7ad8110dc17 --- /dev/null +++ b/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/OkeWorkloadBuilderProvider.java @@ -0,0 +1,52 @@ +/* + * 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.authentication.okeworkload; + +import java.net.URI; +import java.util.function.Supplier; + +import io.helidon.integrations.oci.OciConfig; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider; +import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider.OkeWorkloadIdentityAuthenticationDetailsProviderBuilder; + +/** + * OKE Workload builder provider, uses the + * {@link OkeWorkloadIdentityAuthenticationDetailsProviderBuilder}. + */ +@Service.Provider +class OkeWorkloadBuilderProvider implements Supplier { + private final OciConfig config; + + OkeWorkloadBuilderProvider(OciConfig config) { + this.config = config; + } + + @Override + public OkeWorkloadIdentityAuthenticationDetailsProviderBuilder get() { + var builder = OkeWorkloadIdentityAuthenticationDetailsProvider.builder() + .timeoutForEachRetry((int) config.authenticationTimeout().toMillis()); + + config.imdsBaseUri() + .map(URI::toString) + .ifPresent(builder::metadataBaseUrl); + + return builder; + } + +} diff --git a/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/package-info.java b/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/package-info.java new file mode 100644 index 00000000000..e0a456f85eb --- /dev/null +++ b/integrations/oci/authentication/oke-workload/src/main/java/io/helidon/integrations/oci/authentication/okeworkload/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. + */ + +/** + * Support for OCI authentication method based on OKE workload identity. + */ +package io.helidon.integrations.oci.authentication.okeworkload; diff --git a/integrations/oci/authentication/oke-workload/src/main/java/module-info.java b/integrations/oci/authentication/oke-workload/src/main/java/module-info.java new file mode 100644 index 00000000000..3387db8f717 --- /dev/null +++ b/integrations/oci/authentication/oke-workload/src/main/java/module-info.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Support for OCI authentication method based on OKE workload identity. + */ +module io.helidon.integrations.oci.authentication.okeworkload { + requires io.helidon.common; + requires io.helidon.service.registry; + requires io.helidon.integrations.oci; + + requires oci.java.sdk.common; + requires oci.java.sdk.addons.oke.workload.identity; + + exports io.helidon.integrations.oci.authentication.okeworkload; +} diff --git a/integrations/oci/authentication/pom.xml b/integrations/oci/authentication/pom.xml new file mode 100644 index 00000000000..ab0aa0721c6 --- /dev/null +++ b/integrations/oci/authentication/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + + io.helidon.integrations.oci + helidon-integrations-oci-project + 4.1.0-SNAPSHOT + + pom + + io.helidon.integrations.oci.authentication + helidon-integrations-oci-authentication-project + Helidon Integrations OCI Authentication Project + + + Additional OCI Authentication providers + + + + instance + resource + oke-workload + + diff --git a/integrations/oci/authentication/resource/pom.xml b/integrations/oci/authentication/resource/pom.xml new file mode 100644 index 00000000000..3a20556adc3 --- /dev/null +++ b/integrations/oci/authentication/resource/pom.xml @@ -0,0 +1,108 @@ + + + + 4.0.0 + + io.helidon.integrations.oci.authentication + helidon-integrations-oci-authentication-project + 4.1.0-SNAPSHOT + + helidon-integrations-oci-authentication-resource + Helidon Integrations OCI Resource Instance + + + Authentication based on Resource principal + + + + + io.helidon.integrations.oci + helidon-integrations-oci + + + io.helidon.service + helidon-service-registry + + + 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.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + io.helidon.codegen + helidon-codegen-apt + ${helidon.version} + + + io.helidon.service + helidon-service-codegen + ${helidon.version} + + + io.helidon.codegen + helidon-codegen-helidon-copyright + ${helidon.version} + + + + + + diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java b/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/AuthenticationMethodResourcePrincipal.java similarity index 65% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java rename to integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/AuthenticationMethodResourcePrincipal.java index 2b856fc00a5..cb0da238fcd 100644 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyResourcePrincipal.java +++ b/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/AuthenticationMethodResourcePrincipal.java @@ -14,41 +14,41 @@ * limitations under the License. */ -package io.helidon.integrations.oci; +package io.helidon.integrations.oci.authentication.resource; 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.integrations.oci.spi.OciAuthenticationMethod; import io.helidon.service.registry.Service; import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; -import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider; +import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider.ResourcePrincipalAuthenticationDetailsProviderBuilder; /** - * Resource authentication strategy, uses the {@link com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider}. + * Resource principal authentication method, 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"; +class AuthenticationMethodResourcePrincipal implements OciAuthenticationMethod { + private static final System.Logger LOGGER = System.getLogger(AuthenticationMethodResourcePrincipal.class.getName()); + private static final String RESOURCE_PRINCIPAL_VERSION_ENV_VAR = "OCI_RESOURCE_PRINCIPAL_VERSION"; + private static final String METHOD = "resource-principal"; + - private static final System.Logger LOGGER = System.getLogger(AtnStrategyResourcePrincipal.class.getName()); private final LazyValue> provider; - AtnStrategyResourcePrincipal(OciConfig config) { - provider = createProvider(config); + AuthenticationMethodResourcePrincipal(ResourcePrincipalAuthenticationDetailsProviderBuilder builder) { + provider = createProvider(builder); } @Override - public String strategy() { - return STRATEGY; + public String method() { + return METHOD; } @Override @@ -56,7 +56,9 @@ public Optional provider() { return provider.get(); } - private static LazyValue> createProvider(OciConfig config) { + private static LazyValue> + createProvider(ResourcePrincipalAuthenticationDetailsProviderBuilder builder) { + 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) { @@ -66,14 +68,6 @@ private static LazyValue> create } 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/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/ResourcePrincipalBuilderProvider.java b/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/ResourcePrincipalBuilderProvider.java new file mode 100644 index 00000000000..3967bb8ca99 --- /dev/null +++ b/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/ResourcePrincipalBuilderProvider.java @@ -0,0 +1,52 @@ +/* + * 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.authentication.resource; + +import java.net.URI; +import java.util.function.Supplier; + +import io.helidon.integrations.oci.OciConfig; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider; +import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider.ResourcePrincipalAuthenticationDetailsProviderBuilder; + +/** + * Instance principal builder provider, uses the + * {@link ResourcePrincipalAuthenticationDetailsProviderBuilder}. + */ +@Service.Provider +class ResourcePrincipalBuilderProvider implements Supplier { + private final OciConfig config; + + ResourcePrincipalBuilderProvider(OciConfig config) { + this.config = config; + } + + @Override + public ResourcePrincipalAuthenticationDetailsProviderBuilder get() { + var builder = ResourcePrincipalAuthenticationDetailsProvider.builder() + .timeoutForEachRetry((int) config.authenticationTimeout().toMillis()); + + config.imdsBaseUri() + .map(URI::toString) + .ifPresent(builder::metadataBaseUrl); + + return builder; + } + +} diff --git a/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/package-info.java b/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/package-info.java new file mode 100644 index 00000000000..11d321b819f --- /dev/null +++ b/integrations/oci/authentication/resource/src/main/java/io/helidon/integrations/oci/authentication/resource/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. + */ + +/** + * Support for OCI authentication method based on resource principal. + */ +package io.helidon.integrations.oci.authentication.resource; diff --git a/integrations/oci/authentication/resource/src/main/java/module-info.java b/integrations/oci/authentication/resource/src/main/java/module-info.java new file mode 100644 index 00000000000..bbf63daeafb --- /dev/null +++ b/integrations/oci/authentication/resource/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/** + * Support for OCI authentication method based on resource principal. + */ +module io.helidon.integrations.oci.authentication.resource { + requires io.helidon.common; + requires io.helidon.service.registry; + requires io.helidon.integrations.oci; + + requires oci.java.sdk.common; + + exports io.helidon.integrations.oci.authentication.resource; +} diff --git a/integrations/oci/oci/README.md b/integrations/oci/oci/README.md index 21e5204a2ca..588c557605d 100644 --- a/integrations/oci/oci/README.md +++ b/integrations/oci/oci/README.md @@ -6,31 +6,68 @@ The module uses (internally) a service of type `OciConfig`. This instance is use 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. +The configuration options (main): +```yaml +helidon.oci: + authentication-method: "auto" # can select a specific authentication method to use, use auto to choose from available + allowed-authentication-methods: ["config", "config-file"] # limit the list of authentication methods to try with auto + authentication: # specific configuration of authentication methods + config-file: # all details in a config file + profile: "DEFAULT" # optional + path: "/custom/path/.oci/config" # optional + config: # all details here in config + region: "some-region-id" + fingerprint: "0A0B..." + tenant-id: "ocid" + user-id: "ocid" + private-key: + resource-path: "/on/classpath/private-key.pem" + session-token: # same as config + session token and some additional configuration + session-token: "token" + session-lifetime-hours: 8 +``` + +This module provides a few 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 provider is looked up by using instances of `io.helidon.integrations.oci.spi.OciAuthenticationMethod`. 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 +| Provider | Weight | Description | +|--------------------|--------|---------------------------------------------------------------| +| Config | 90 | Uses `OciConfig` | +| Session Token | 85 | Uses `OciConfig` or config file, if it contains session token | +| Config File | 80 | Uses `~/.oci/config` file | +| Resource Principal | 70 | Resource principal (such as functions) | +| Instance Principal | 60 | Principal of the compute instance | 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). +## Authentication Details Provider Builders + +The following builders can be used as dependencies (and discovered from `ServiceRegistry`). The builders will have as much +information as available filled in: + +- `com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider.SessionTokenAuthenticationDetailsProviderBuilder` +- `com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder` +- `com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider.ResourcePrincipalAuthenticationDetailsProviderBuilder` +- `com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider.OkeWorkloadIdentityAuthenticationDetailsProviderBuilder` + ## Region Any service can have a dependency on `com.oracle.bmc.Region`, and it can be looked up using Service Registry lookup methods. +Important note: authentication details providers MUST NOT have a dependency on `Region`, as it may be provided by authentication +details provider (this would create a cyclic dependency). + 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. 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/AdpProvider.java similarity index 57% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnDetailsProvider.java rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AdpProvider.java index 9a870d6383c..2f850cc62f3 100644 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnDetailsProvider.java +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AdpProvider.java @@ -23,25 +23,25 @@ import io.helidon.common.LazyValue; import io.helidon.common.config.ConfigException; -import io.helidon.integrations.oci.spi.OciAtnStrategy; +import io.helidon.integrations.oci.spi.OciAuthenticationMethod; import io.helidon.service.registry.Service; import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; @Service.Provider @Service.ExternalContracts(AbstractAuthenticationDetailsProvider.class) -class AtnDetailsProvider implements Supplier> { +class AdpProvider implements Supplier> { private final LazyValue> provider; - AtnDetailsProvider(OciConfig ociConfig, List atnDetailsProviders) { - String chosenStrategy = ociConfig.atnStrategy(); + AdpProvider(OciConfig ociConfig, List atnDetailsProviders) { + String chosenAtnMethod = ociConfig.authenticationMethod(); LazyValue> providerLazyValue = null; - if (OciConfigBlueprint.STRATEGY_AUTO.equals(chosenStrategy)) { + if (OciConfigBlueprint.AUTHENTICATION_METHOD_AUTO.equals(chosenAtnMethod)) { // auto, chose from existing providerLazyValue = LazyValue.create(() -> { - for (OciAtnStrategy atnDetailsProvider : atnDetailsProviders) { + for (OciAuthenticationMethod atnDetailsProvider : atnDetailsProviders) { Optional provider = atnDetailsProvider.provider(); if (provider.isPresent()) { return provider; @@ -52,22 +52,18 @@ class AtnDetailsProvider implements Supplier 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.")))); + for (OciAuthenticationMethod atnDetailsProvider : atnDetailsProviders) { + if (chosenAtnMethod.equals(atnDetailsProvider.method())) { + providerLazyValue = LazyValue.create(() -> toProvider(atnDetailsProvider, chosenAtnMethod)); break; } - strategies.add(atnDetailsProvider.strategy()); + strategies.add(atnDetailsProvider.method()); } 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); + throw new ConfigException("There is an OCI authentication method chosen: \"" + chosenAtnMethod + + "\", yet there is not provider that can provide this method. Supported " + + "methods: " + strategies); } } @@ -78,4 +74,12 @@ class AtnDetailsProvider implements Supplier get() { return provider.get(); } + + private Optional toProvider(OciAuthenticationMethod atnDetailsProvider, + String chosenMethod) { + return Optional.of(atnDetailsProvider.provider() + .orElseThrow(() -> new ConfigException( + "Authentication method \"" + chosenMethod + "\" did not provide an " + + "authentication provider, yet it is requested through configuration."))); + } } diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AdpSessionTokenBuilderProvider.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AdpSessionTokenBuilderProvider.java new file mode 100644 index 00000000000..b20b600864e --- /dev/null +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AdpSessionTokenBuilderProvider.java @@ -0,0 +1,140 @@ +/* + * 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.nio.file.Path; +import java.time.Duration; +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import io.helidon.common.LazyValue; +import io.helidon.common.config.ConfigException; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.ConfigFileReader.ConfigFile; +import com.oracle.bmc.Region; +import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; +import com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider; +import com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider.SessionTokenAuthenticationDetailsProviderBuilder; + +@Service.Provider +class AdpSessionTokenBuilderProvider implements Supplier { + private static final LazyValue DEFAULT_SCHEDULER = + LazyValue.create(Executors::newSingleThreadScheduledExecutor); + + private final OciConfig config; + private final Supplier> configFileSupplier; + + AdpSessionTokenBuilderProvider(OciConfig config, + Supplier> configFileSupplier) { + + this.config = config; + this.configFileSupplier = configFileSupplier; + } + + @Override + public SessionTokenAuthenticationDetailsProviderBuilder get() { + var builder = SessionTokenAuthenticationDetailsProvider.builder(); + + updateFromConfigFile(builder); + updateFromConfig(builder); + + return builder; + } + + Optional value(ConfigFile file, String key) { + return Optional.ofNullable(file.get(key)); + } + + private void updateFromConfig(SessionTokenAuthenticationDetailsProviderBuilder builder) { + Optional maybeSessionTokenConfig = config.sessionTokenMethodConfig(); + if (maybeSessionTokenConfig.isEmpty()) { + return; + } + SessionTokenMethodConfig sessionTokenConfig = maybeSessionTokenConfig.get(); + + builder.fingerprint(sessionTokenConfig.fingerprint()); + sessionTokenConfig.passphrase() + .map(String::new) + .ifPresent(builder::passPhrase); + sessionTokenConfig.privateKeyPath() + .map(Path::toString) + .ifPresent(builder::privateKeyFilePath); + + builder.region(sessionTokenConfig.region()); + builder.tenantId(sessionTokenConfig.tenantId()); + builder.userId(sessionTokenConfig.userId()); + + builder.timeUnit(TimeUnit.MILLISECONDS); + sessionTokenConfig.initialRefreshDelay() + .map(Duration::toMillis) + .ifPresent(builder::initialRefreshDelay); + + sessionTokenConfig.refreshPeriod() + .map(Duration::toMillis) + .ifPresent(builder::refreshPeriod); + sessionTokenConfig.sessionLifetimeHours() + .ifPresent(builder::sessionLifetimeHours); + + builder.scheduler(sessionTokenConfig.scheduler().orElseGet(DEFAULT_SCHEDULER)); + + /* + Session token + */ + Optional sessionToken = sessionTokenConfig.sessionToken(); + Optional sessionTokenPath = sessionTokenConfig.sessionTokenPath(); + + if (sessionToken.isEmpty() && sessionTokenPath.isEmpty()) { + throw new ConfigException("When configuring session token authentication, either session token or session token " + + "path must be provided"); + } + if (sessionToken.isPresent()) { + builder.sessionToken(sessionToken.get()); + } else { + builder.sessionTokenFilePath(sessionTokenPath.get().toString()); + } + } + + private void updateFromConfigFile(SessionTokenAuthenticationDetailsProviderBuilder builder) { + Optional maybeConfigFile = configFileSupplier.get(); + if (maybeConfigFile.isEmpty()) { + return; + } + ConfigFile configFile = maybeConfigFile.get(); + + value(configFile, "security_token_file") + .ifPresent(builder::sessionTokenFilePath); + value(configFile, "tenancy") + .ifPresent(builder::tenantId); + value(configFile, "key_file") + .ifPresent(builder::privateKeyFilePath); + value(configFile, "fingerprint") + .ifPresent(builder::fingerprint); + value(configFile, "pass_phrase") + .ifPresent(builder::passPhrase); + value(configFile, "user") + .ifPresent(builder::userId); + + Region region = ConfigFileAuthenticationDetailsProvider.getRegionFromConfigFile(configFile); + if (region != null) { + builder.region(region); + } + } +} 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 deleted file mode 100644 index ae39f4e9f34..00000000000 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfigFile.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5a9730f108b..00000000000 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyInstancePrincipal.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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/AtnStrategyConfig.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodConfig.java similarity index 68% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfig.java rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodConfig.java index 8c33237de6f..2be49bd2136 100644 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AtnStrategyConfig.java +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodConfig.java @@ -16,13 +16,14 @@ package io.helidon.integrations.oci; +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.common.configurable.Resource; -import io.helidon.integrations.oci.spi.OciAtnStrategy; +import io.helidon.integrations.oci.spi.OciAuthenticationMethod; import io.helidon.service.registry.Service; import com.oracle.bmc.Region; @@ -31,26 +32,34 @@ import com.oracle.bmc.auth.SimplePrivateKeySupplier; /** - * Config based authentication strategy, uses the {@link com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider}. + * Config based authentication method, uses the {@link com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider}. */ @Weight(Weighted.DEFAULT_WEIGHT - 10) @Service.Provider -class AtnStrategyConfig implements OciAtnStrategy { - static final String STRATEGY = "config"; +class AuthenticationMethodConfig implements OciAuthenticationMethod { + static final String METHOD = "config"; + + private static final System.Logger LOGGER = System.getLogger(AuthenticationMethodConfig.class.getName()); private final LazyValue> provider; - AtnStrategyConfig(OciConfig config) { - provider = config.configStrategyConfig() - .map(configStrategyConfigBlueprint -> LazyValue.create(() -> { - return Optional.of(createProvider(configStrategyConfigBlueprint)); + AuthenticationMethodConfig(OciConfig config) { + provider = config.configMethodConfig() + .map(configMethodConfigBlueprint -> LazyValue.create(() -> { + return Optional.of(createProvider(configMethodConfigBlueprint)); })) - .orElseGet(() -> LazyValue.create(Optional.empty())); + .orElseGet(() -> { + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, "Configuration for Config based authentication details provider is" + + " not available."); + } + return LazyValue.create(Optional.empty()); + }); } @Override - public String strategy() { - return STRATEGY; + public String method() { + return METHOD; } @Override @@ -58,7 +67,7 @@ public Optional provider() { return provider.get(); } - private static AbstractAuthenticationDetailsProvider createProvider(ConfigStrategyConfigBlueprint config) { + private static AbstractAuthenticationDetailsProvider createProvider(ConfigMethodConfigBlueprint config) { Region region = Region.fromRegionCodeOrId(config.region()); var builder = SimpleAuthenticationDetailsProvider.builder(); @@ -83,11 +92,12 @@ private static AbstractAuthenticationDetailsProvider createProvider(ConfigStrate builder.privateKeySupplier(new SimplePrivateKeySupplier(keyFile)); } + config.passphrase().ifPresent(builder::passphraseCharacters); + 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/AuthenticationMethodConfigFile.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodConfigFile.java new file mode 100644 index 00000000000..4635a8f9fd8 --- /dev/null +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodConfigFile.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.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.OciAuthenticationMethod; +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 method, uses the {@link com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider}. + */ +@Weight(Weighted.DEFAULT_WEIGHT - 20) +@Service.Provider +class AuthenticationMethodConfigFile implements OciAuthenticationMethod { + static final String METHOD = "config-file"; + + private final LazyValue> provider; + + AuthenticationMethodConfigFile(Supplier> configFile) { + provider = createProvider(configFile); + } + + @Override + public String method() { + return METHOD; + } + + @Override + public Optional provider() { + return provider.get(); + } + + private static LazyValue> + createProvider(Supplier> configFileSupplier) { + + return LazyValue.create(() -> configFileSupplier.get() + .map(ConfigFileAuthenticationDetailsProvider::new)); + } +} diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodSessionToken.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodSessionToken.java new file mode 100644 index 00000000000..b4854adf880 --- /dev/null +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/AuthenticationMethodSessionToken.java @@ -0,0 +1,102 @@ +/* + * 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 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.OciAuthenticationMethod; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.ConfigFileReader; +import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; +import com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider.SessionTokenAuthenticationDetailsProviderBuilder; + +/** + * Session token authentication method, uses the {@link com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider}. + */ +@Weight(Weighted.DEFAULT_WEIGHT - 15) +@Service.Provider +class AuthenticationMethodSessionToken implements OciAuthenticationMethod { + static final String DEFAULT_PROFILE_NAME = "DEFAULT"; + static final String METHOD = "session-token"; + + private static final System.Logger LOGGER = System.getLogger(AuthenticationMethodSessionToken.class.getName()); + + private final LazyValue> provider; + + AuthenticationMethodSessionToken(OciConfig config, + Supplier> configFileSupplier, + Supplier builder) { + provider = LazyValue.create(() -> createProvider(config, configFileSupplier, builder)); + } + + @Override + public String method() { + return METHOD; + } + + @Override + public Optional provider() { + return provider.get(); + } + + private static Optional + createProvider(OciConfig config, + Supplier> configFileSupplier, + Supplier builder) { + + /* + Session tokens provide is available if either of the following is true: + - there is authentication.session-token configuration + - there is an OCI config file, and it contains security_token_file + */ + + Optional maybeConfigFile = configFileSupplier.get(); + Optional maybeSessionTokenConfig = config.sessionTokenMethodConfig(); + + if (hasSecurityToken(maybeConfigFile) || maybeSessionTokenConfig.isPresent()) { + try { + return Optional.of(builder.get().build()); + } catch (IOException e) { + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, "Cannot create session token authentication provider", e); + } + return Optional.empty(); + } + } + + if (LOGGER.isLoggable(Level.TRACE)) { + LOGGER.log(Level.TRACE, "Session token authentication provider is not configured"); + } + return Optional.empty(); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + private static boolean hasSecurityToken(Optional maybeConfigFile) { + if (maybeConfigFile.isPresent()) { + ConfigFileReader.ConfigFile configFile = maybeConfigFile.get(); + return configFile.get("security_token_file") != null; + } + return false; + } +} 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/ConfigFileMethodConfigBlueprint.java similarity index 91% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileStrategyConfigBlueprint.java rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileMethodConfigBlueprint.java index 7ee84c7fb75..5a1a6b3f2da 100644 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileStrategyConfigBlueprint.java +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileMethodConfigBlueprint.java @@ -23,7 +23,7 @@ @Prototype.Blueprint @Prototype.Configured -interface ConfigFileStrategyConfigBlueprint { +interface ConfigFileMethodConfigBlueprint { /** * The OCI configuration profile path. * @@ -38,6 +38,6 @@ interface ConfigFileStrategyConfigBlueprint { * @return the optional OCI configuration/auth profile name */ @Option.Configured - @Option.Default(AtnStrategyConfigFile.DEFAULT_PROFILE_NAME) + @Option.Default(ConfigFileProvider.DEFAULT_PROFILE_NAME) String profile(); } diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileProvider.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileProvider.java new file mode 100644 index 00000000000..f2ff288f4d7 --- /dev/null +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigFileProvider.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.integrations.oci; + +import java.io.IOException; +import java.util.Optional; +import java.util.function.Supplier; + +import io.helidon.common.LazyValue; +import io.helidon.service.registry.Service; + +import com.oracle.bmc.ConfigFileReader; + +@Service.Provider +@Service.ExternalContracts(ConfigFileReader.ConfigFile.class) +class ConfigFileProvider implements Supplier> { + static final String DEFAULT_PROFILE_NAME = "DEFAULT"; + + private static final System.Logger LOGGER = System.getLogger(ConfigFileProvider.class.getName()); + + private final LazyValue> value; + + ConfigFileProvider(OciConfig config) { + value = LazyValue.create(() -> findConfigFile(config)); + } + + @Override + public Optional get() { + return value.get(); + } + + private Optional findConfigFile(OciConfig config) { + var atnMethodConfig = config.configFileMethodConfig(); + // there are two options to override - the path to config file, and the profile + String profile = atnMethodConfig.map(ConfigFileMethodConfigBlueprint::profile) + .orElse(DEFAULT_PROFILE_NAME); + String configFilePath = atnMethodConfig.flatMap(ConfigFileMethodConfigBlueprint::path) + .orElse(null); + + try { + ConfigFileReader.ConfigFile configFile; + if (configFilePath == null) { + configFile = ConfigFileReader.parseDefault(profile); + } else { + configFile = ConfigFileReader.parse(configFilePath, profile); + } + return Optional.of(configFile); + } catch (IOException e) { + if (LOGGER.isLoggable(System.Logger.Level.TRACE)) { + LOGGER.log(System.Logger.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/ConfigStrategyConfigBlueprint.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigMethodConfigBlueprint.java similarity index 95% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigStrategyConfigBlueprint.java rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigMethodConfigBlueprint.java index 968688f1587..a8561c062dd 100644 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigStrategyConfigBlueprint.java +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/ConfigMethodConfigBlueprint.java @@ -23,11 +23,11 @@ import io.helidon.common.configurable.Resource; /** - * Configuration of the {@code config} authentication strategy. + * Configuration of the {@code config} authentication method. */ @Prototype.Blueprint @Prototype.Configured -interface ConfigStrategyConfigBlueprint { +interface ConfigMethodConfigBlueprint { /** * The OCI region. * @@ -70,7 +70,7 @@ interface ConfigStrategyConfigBlueprint { */ @Option.Configured @Option.Confidential - char[] passphrase(); + Optional passphrase(); /** * The OCI tenant id. diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/HelidonOci.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/HelidonOci.java new file mode 100644 index 00000000000..b843797618f --- /dev/null +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/HelidonOci.java @@ -0,0 +1,58 @@ +/* + * 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.net.InetAddress; +import java.net.URI; +import java.time.Duration; + +import static io.helidon.integrations.oci.OciConfigSupport.IMDS_HOSTNAME; + +/** + * Helper methods for OCI integration. + */ +public final class HelidonOci { + private static final System.Logger LOGGER = System.getLogger(HelidonOci.class.getName()); + + private HelidonOci() { + } + + /** + * Check whether IMDS (metadata service endpoint) is available or not. + * + * @param config OCI meta configuration, allowing a customized IMDS endpoint + * @return whether the metadata service is available + */ + public 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(System.Logger.Level.TRACE, + "IMDS service is not reachable, or timed out for address: " + + IMDS_HOSTNAME + ".", + e); + return false; + } + } +} 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 index cdc554afbeb..1dad85f8fa3 100644 --- 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 @@ -23,6 +23,7 @@ import io.helidon.builder.api.Option; import io.helidon.builder.api.Prototype; +import io.helidon.common.config.Config; import com.oracle.bmc.Region; @@ -36,10 +37,10 @@ @Prototype.CustomMethods(OciConfigSupport.class) interface OciConfigBlueprint { /** - * Default authentication strategy. The default is to use automatic discovery - i.e. cycle through possible + * Default authentication method. 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"; + String AUTHENTICATION_METHOD_AUTO = "auto"; /** * Explicit region. The configured region will be used by region provider. @@ -51,58 +52,70 @@ interface OciConfigBlueprint { Optional region(); /** - * Authentication strategy to use. If the configured strategy is not available, an exception + * Authentication method to use. If the configured method 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} - + *
    • {@value #AUTHENTICATION_METHOD_AUTO} - use the list of {@link io.helidon.integrations.oci.OciConfig#allowedAuthenticationMethods()} + * (in the provided order), and choose the first one capable of providing data
    • + *
    • {@value AuthenticationMethodConfig#METHOD} - * 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 + *
    • {@value AuthenticationMethodConfigFile#METHOD} - 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 + *
    • {@code resource-principal} - 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}
    • + * {@link com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider}, and is available in a + * separate module {@code helidon-integrations-oci-authentication-resource} + *
    • {@code instance-principal} - use identity of the OCI instance the service is running on, uses + * {@link com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider}, and is available in a + * separate module {@code helidon-integrations-oci-authentication-resource}
    • + *
    • {@code workload} - use workload identity of the OCI Kubernetes workload, available in a + * separate module {@code helidon-integrations-oci-authentication-workload}
    • *
    * - * @return the authentication strategy to apply + * @return the authentication method to apply */ @Option.Configured - @Option.Default(STRATEGY_AUTO) - String atnStrategy(); + @Option.Default(AUTHENTICATION_METHOD_AUTO) + String authenticationMethod(); /** - * List of attempted authentication strategies in case {@link #atnStrategy()} is set to {@value #STRATEGY_AUTO}. + * List of attempted authentication strategies in case {@link io.helidon.integrations.oci.OciConfig#authenticationMethod()} is set + * to {@value #AUTHENTICATION_METHOD_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() + * @see io.helidon.integrations.oci.OciConfig#authenticationMethod() */ @Option.Configured - List allowedAtnStrategies(); + List allowedAuthenticationMethods(); /** - * Config strategy configuration (if provided and used). + * Config method configuration (if provided and used). * - * @return information needed for config {@link #atnStrategy()} + * @return information needed for config {@link io.helidon.integrations.oci.OciConfig#authenticationMethod()} */ - @Option.Configured("config-strategy") - Optional configStrategyConfig(); + @Option.Configured("authentication.config") + Optional configMethodConfig(); /** - * Config file strategy configuration (if provided and used). + * Config file method configuration (if provided and used). * - * @return information to customize config for {@link #atnStrategy()} + * @return information to customize config for {@link io.helidon.integrations.oci.OciConfig#authenticationMethod()} */ - @Option.Configured("config-file-strategy") - Optional configFileStrategyConfig(); + @Option.Configured("authentication.config-file") + Optional configFileMethodConfig(); + + /** + * Session token method configuration (if provided and used). + * + * @return information to customize config for {@link io.helidon.integrations.oci.OciConfig#authenticationMethod()} + */ + @Option.Configured("authentication.session-token") + Optional sessionTokenMethodConfig(); /** * The OCI IMDS connection timeout. This is used to auto-detect availability. @@ -116,7 +129,7 @@ interface OciConfigBlueprint { Duration imdsTimeout(); /** - * The OCI IMDS URI (http URL pointing to the metadata service, if customization needed. + * The OCI IMDS URI (http URL pointing to the metadata service, if customization needed). * * @return the OCI IMDS URI */ @@ -132,5 +145,12 @@ interface OciConfigBlueprint { */ @Option.Configured @Option.Default("PT10S") - Duration atnTimeout(); + Duration authenticationTimeout(); + + /** + * Get the config used to update the builder. + * + * @return configuration + */ + Optional config(); } 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/RegionProviderAuthenticationMethod.java similarity index 83% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAtnStrategy.java rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAuthenticationMethod.java index f8707b31726..db5dbb4142e 100644 --- a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAtnStrategy.java +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/RegionProviderAuthenticationMethod.java @@ -29,12 +29,16 @@ import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider; import com.oracle.bmc.auth.RegionProvider; +/** + * Region provider that uses an available OCI authentication method, if it yields an + * authentication details provider that implements a region provider. + */ @Service.Provider @Weight(Weighted.DEFAULT_WEIGHT - 20) -class RegionProviderAtnStrategy implements OciRegion { +class RegionProviderAuthenticationMethod implements OciRegion { private final LazyValue> region; - RegionProviderAtnStrategy(Supplier> atnProvider) { + RegionProviderAuthenticationMethod(Supplier> atnProvider) { this.region = LazyValue.create(() -> { var provider = atnProvider.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 index 9d8034a8abe..123c06881fe 100644 --- 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 @@ -42,7 +42,7 @@ class RegionProviderSdk implements OciRegion { * We want a different way to get the region if available. */ static Region regionFromImds(OciConfig ociConfig) { - if (AtnStrategyInstancePrincipal.imdsAvailable(ociConfig)) { + if (HelidonOci.imdsAvailable(ociConfig)) { Optional uri = ociConfig.imdsBaseUri(); return uri.map(URI::toString) .map(Region::getRegionFromImds) diff --git a/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/SessionTokenMethodConfigBlueprint.java b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/SessionTokenMethodConfigBlueprint.java new file mode 100644 index 00000000000..70d8350833a --- /dev/null +++ b/integrations/oci/oci/src/main/java/io/helidon/integrations/oci/SessionTokenMethodConfigBlueprint.java @@ -0,0 +1,154 @@ +/* + * 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.nio.file.Path; +import java.time.Duration; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; + +import io.helidon.builder.api.Option; +import io.helidon.builder.api.Prototype; + +/** + * Configuration of the {@code config} authentication method. + */ +@Prototype.Blueprint +@Prototype.Configured +interface SessionTokenMethodConfigBlueprint { + /** + * 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 {@link com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider#getFingerprint()} for more details. + * + * @return the OCI authentication fingerprint + */ + @Option.Configured + String fingerprint(); + + /** + * 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 + Optional 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(); + + /** + * 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/sessions/DEFAULT/oci_api_key.pem} file in user home directory. + * + * @return the OCI authentication key file + */ + @Option.Configured + Optional privateKeyPath(); + + /** + * Session token path. + * If both this value, and {@link #sessionToken()} is defined, the value of {@link #sessionToken()} is used. + * + * @return session token path + */ + @Option.Configured + Optional sessionTokenPath(); + + /** + * Session token value. + * If both this value, and {@link #sessionTokenPath()} is defined, this value is used. + * + * @return session token + */ + @Option.Configured + Optional sessionToken(); + + /** + * Delay of the first refresh. + * Defaults to 0, to refresh immediately (implemented in the authentication details provider). + * + * @return initial refresh delay + * @see com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider.SessionTokenAuthenticationDetailsProviderBuilder#initialRefreshDelay(long) + */ + @Option.Configured + Optional initialRefreshDelay(); + + /** + * Refresh period, i.e. how often refresh occurs. + * Defaults to 55 minutes (implemented in the authentication details provider). + * + * @return refresh period + * @see com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider.SessionTokenAuthenticationDetailsProviderBuilder#refreshPeriod(long) + */ + @Option.Configured + Optional refreshPeriod(); + + /** + * Maximal lifetime of a session. + * Defaults to (and maximum is) 24 hours. + * Can only be set to a lower value. + * + * @return lifetime of a session in hours + */ + @Option.Configured + Optional sessionLifetimeHours(); + + /** + * Customize the scheduled executor service to use for scheduling. + * Defaults to a single thread executor service. + * + * @return scheduled executor service + */ + Optional scheduler(); +} 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/OciAuthenticationMethod.java similarity index 79% rename from integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciAtnStrategy.java rename to integrations/oci/oci/src/main/java/io/helidon/integrations/oci/spi/OciAuthenticationMethod.java index 44f324dbfab..0ca6d2600cd 100644 --- 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/OciAuthenticationMethod.java @@ -27,22 +27,22 @@ *

    * This service is implemented by: *

      - *
    • Config based strategy
    • - *
    • Config file based strategy
    • - *
    • Resource principal strategy
    • - *
    • Instance principal strategy
    • + *
    • Config based method
    • + *
    • Config file based method
    • + *
    • Resource principal method
    • + *
    • Instance principal method
    • *
    * 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 { +public interface OciAuthenticationMethod { /** - * The strategy name, can be used to explicitly select a strategy using configuration. + * The OCI authentication method name, can be used to explicitly select a method using configuration. * - * @return strategy name + * @return OCI authentication method name */ - String strategy(); + String method(); /** * Provide an instance of the {@link com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider} to be used 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 index 858027753d0..e57786e087c 100644 --- 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 @@ -17,7 +17,7 @@ /** * Extension points for OCI integration. * - * @see io.helidon.integrations.oci.spi.OciAtnStrategy + * @see io.helidon.integrations.oci.spi.OciAuthenticationMethod * @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 index 0175cccaa98..91b06eecdd3 100644 --- a/integrations/oci/oci/src/main/java/module-info.java +++ b/integrations/oci/oci/src/main/java/module-info.java @@ -29,14 +29,14 @@ *
  • 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}
  • + * such as {@code OCI_AUTHENTICATION_METHOD=config_file} *
  • Add system properties to override configuration options of {@link io.helidon.integrations.oci.OciConfig}, - * such as {@code oci.atnStrategy=config_file}
  • + * such as {@code oci.authenticationMethod=config_file} * * - * To customize authentication details provider, you can implement {@link io.helidon.integrations.oci.spi.OciAtnStrategy} + * To customize authentication details provider, you can implement {@link io.helidon.integrations.oci.spi.OciAuthenticationMethod} * service. The out-of-the-box providers have all less than default weight, and are in the - * following order (strategy: description (weight)): + * following order (authentication method: description (weight)): *